mirror of
https://github.com/ScoopInstaller/Scoop.git
synced 2025-10-30 14:17:54 +00:00
Compare commits
305 Commits
2019-10-18
...
v0.2.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
7d5a47cc77 | ||
|
|
04b30de9dd | ||
|
|
750ea54d49 | ||
|
|
43d5e99705 | ||
|
|
63b858c41f | ||
|
|
59328fc83c | ||
|
|
6bb5e932fe | ||
|
|
3af0cdbc21 | ||
|
|
765d3aad7e | ||
|
|
b740e98c36 | ||
|
|
a8e36d35c7 | ||
|
|
9024ee450c | ||
|
|
e4580dd705 | ||
|
|
a67937f77d | ||
|
|
aa61f1994a | ||
|
|
0cb6152e1a | ||
|
|
4047d6962c | ||
|
|
40b2d951e8 | ||
|
|
b95ccbe14a | ||
|
|
e1f569b01b | ||
|
|
d1f828c942 | ||
|
|
26517644df | ||
|
|
f83fa145de | ||
|
|
21c5c6e775 | ||
|
|
c41cb84404 | ||
|
|
f24159c8f1 | ||
|
|
14854c3548 | ||
|
|
3345a5ed10 | ||
|
|
53f56e350f | ||
|
|
ef2bfeb3f2 | ||
|
|
b2a27f420d | ||
|
|
c01960f283 | ||
|
|
285ffd0322 | ||
|
|
b69bdbd6d2 | ||
|
|
ba970d5c42 | ||
|
|
e450843827 | ||
|
|
f559c813df | ||
|
|
7967905980 | ||
|
|
0e4721a408 | ||
|
|
e0a5313132 | ||
|
|
8c02776c6c | ||
|
|
c6b10c8f89 | ||
|
|
5b0bdaf893 | ||
|
|
ba28a4f4f1 | ||
|
|
3f4c191faa | ||
|
|
4a9efaa2d9 | ||
|
|
98ea7a5e82 | ||
|
|
2644a5f7b1 | ||
|
|
158c0fd4d0 | ||
|
|
e09127f7be | ||
|
|
37f5024194 | ||
|
|
5f407ca434 | ||
|
|
d023e6cf0d | ||
|
|
81b7aaf8d7 | ||
|
|
4e64db7f93 | ||
|
|
6f8bf04ce5 | ||
|
|
c9df8f4017 | ||
|
|
d9f55a3a0a | ||
|
|
d7fb97f517 | ||
|
|
d71fe82f9d | ||
|
|
5b87c99aa8 | ||
|
|
3ca1b1f2e3 | ||
|
|
30f57aee6a | ||
|
|
bc35a563b5 | ||
|
|
25b170895f | ||
|
|
92c89f86ed | ||
|
|
cabaf59f62 | ||
|
|
9142703bce | ||
|
|
00adc0d828 | ||
|
|
399274e242 | ||
|
|
4f5ecd029e | ||
|
|
c864f68c0b | ||
|
|
3c5f5ff20a | ||
|
|
fba658c020 | ||
|
|
00f7859b3f | ||
|
|
5d8aeb54bb | ||
|
|
d5cb86078b | ||
|
|
271d41b949 | ||
|
|
36ae35c606 | ||
|
|
0d1ad20869 | ||
|
|
5602083868 | ||
|
|
2a0187458d | ||
|
|
1dbab1fee8 | ||
|
|
5e11c94a54 | ||
|
|
fb496c482b | ||
|
|
dec2598052 | ||
|
|
af2056a8fd | ||
|
|
f343bd9f5e | ||
|
|
cdba268b47 | ||
|
|
cbe29eddb3 | ||
|
|
cd6d31dae8 | ||
|
|
6f60059035 | ||
|
|
f3cdfffcfe | ||
|
|
b966ca8c63 | ||
|
|
2f7ff1bab5 | ||
|
|
6c6a2ca410 | ||
|
|
02da753fa7 | ||
|
|
b488cb9ab3 | ||
|
|
48b035d7f9 | ||
|
|
386d3be20e | ||
|
|
992e99358a | ||
|
|
c1d48e4853 | ||
|
|
4d5fee36e1 | ||
|
|
2ec00d576c | ||
|
|
37a886947d | ||
|
|
6387b7d1cd | ||
|
|
3c90d1a070 | ||
|
|
ef4349bee4 | ||
|
|
c31ccea971 | ||
|
|
d021438b20 | ||
|
|
ab8be955b4 | ||
|
|
5a1cdcb93d | ||
|
|
4ec9eccdb4 | ||
|
|
07ecd01159 | ||
|
|
3bb7036ee1 | ||
|
|
1d5e81d2dc | ||
|
|
29477d992a | ||
|
|
ac71fccbec | ||
|
|
0f5097be4f | ||
|
|
f34be82516 | ||
|
|
59088a9f00 | ||
|
|
cf3f57caa3 | ||
|
|
3c344682fe | ||
|
|
90fa473262 | ||
|
|
bbad8aef6c | ||
|
|
2bc2e652e3 | ||
|
|
abcb04878c | ||
|
|
e663027299 | ||
|
|
ae89213842 | ||
|
|
6c2b34d29b | ||
|
|
6161f0bf9f | ||
|
|
baa20aef55 | ||
|
|
77d00d1771 | ||
|
|
ef3bf14547 | ||
|
|
5ad8c76dd7 | ||
|
|
47ebc6f176 | ||
|
|
84590f89f4 | ||
|
|
e35ff313a5 | ||
|
|
af7a6f6d0e | ||
|
|
948daa0c63 | ||
|
|
1490869e6f | ||
|
|
dcce404529 | ||
|
|
8bb7390a75 | ||
|
|
2047f8a929 | ||
|
|
458ec9003f | ||
|
|
bae0be9581 | ||
|
|
1e90310838 | ||
|
|
30e7967a04 | ||
|
|
6c340cb7e3 | ||
|
|
35b2a42ede | ||
|
|
818162fe82 | ||
|
|
b174775b71 | ||
|
|
5226f26f18 | ||
|
|
4f5acd7210 | ||
|
|
f1a46e1095 | ||
|
|
3e9a4d4ea0 | ||
|
|
2cf025a80b | ||
|
|
0920050464 | ||
|
|
3d67b7d37c | ||
|
|
63a26584cd | ||
|
|
dc7df618c1 | ||
|
|
8ecdb7cc14 | ||
|
|
227de6cfb8 | ||
|
|
ad061d2a6a | ||
|
|
0948824ec7 | ||
|
|
b0557b647b | ||
|
|
7db0fe9382 | ||
|
|
643cfb0da8 | ||
|
|
c8453767b4 | ||
|
|
5e81d49984 | ||
|
|
9b29bbb711 | ||
|
|
22a59866c7 | ||
|
|
6df80c1aee | ||
|
|
6659c55959 | ||
|
|
91ea657923 | ||
|
|
3188115369 | ||
|
|
22fd6986bf | ||
|
|
078b29bc80 | ||
|
|
33a357241d | ||
|
|
573e0933cf | ||
|
|
96de9c14bb | ||
|
|
7995f99dc1 | ||
|
|
f91968cb16 | ||
|
|
0d721abc2f | ||
|
|
48f121e466 | ||
|
|
eada459be9 | ||
|
|
398ccea2ae | ||
|
|
50df0c52c4 | ||
|
|
e6b355eae0 | ||
|
|
5851eaf762 | ||
|
|
2a91ddb101 | ||
|
|
3279bbfb71 | ||
|
|
efcd3bfa38 | ||
|
|
9c9cc807ba | ||
|
|
4eba120897 | ||
|
|
fe01ed52d5 | ||
|
|
ad9f7c6ff1 | ||
|
|
062e6d7973 | ||
|
|
48bb96a3d8 | ||
|
|
e0c5ac2396 | ||
|
|
7e32139322 | ||
|
|
a9fa775d59 | ||
|
|
5afad4e3d1 | ||
|
|
8ac23f8fbc | ||
|
|
6eb90c9c11 | ||
|
|
eb3d42de8f | ||
|
|
e997017f1a | ||
|
|
8ee45a57dc | ||
|
|
ce3464f2f4 |
@@ -18,3 +18,8 @@ end_of_line = crlf
|
||||
|
||||
[*.{yml, yaml}]
|
||||
indent_size = 2
|
||||
|
||||
# Makefiles require tab indentation
|
||||
[{{M,m,GNU}akefile{,.*},*.mak,*.mk}]
|
||||
indent_style = tab
|
||||
end_of_line = lf
|
||||
|
||||
47
.github/ISSUE_TEMPLATE/Bug_report.md
vendored
Normal file
47
.github/ISSUE_TEMPLATE/Bug_report.md
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
---
|
||||
name: "Bug Report"
|
||||
about: "I am facing some problems."
|
||||
title: '[Bug] '
|
||||
labels: "bug"
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
IMPORTANT:
|
||||
If your problem is related to a specific package, open the issue in the relevant bucket,
|
||||
not here.
|
||||
By opening this issue you confirm that you have searched for similar issues/PRs here already.
|
||||
Failing to do so will most likely result in closing of this issue without any explanation.
|
||||
Incomplete form details below might also result in closing of the issue.
|
||||
-->
|
||||
|
||||
## Bug Report
|
||||
|
||||
#### Current Behavior
|
||||
<!-- A clear and concise description of the behavior. -->
|
||||
|
||||
#### Expected Behavior
|
||||
<!-- A clear and concise description of what you expected to happen. -->
|
||||
|
||||
#### Additional context/output
|
||||
<!-- Add any other context about the problem here. If applicable, paste terminal output here to help explain. -->
|
||||
|
||||
#### Possible Solution
|
||||
<!--- Only if you have suggestions on a fix for the bug -->
|
||||
|
||||
### System details
|
||||
|
||||
**Windows version:** [e.g. 7, 8, 10]
|
||||
|
||||
**OS architecture:** [e.g. 32bit, 64bit]
|
||||
|
||||
**PowerShell version:** [output of `"$($PSVersionTable.PSVersion)"`]
|
||||
|
||||
**Additional software:** [(optional) e.g. ConEmu, Git]
|
||||
|
||||
#### Scoop Configuration
|
||||
<!-- Can be found in ~/.config/scoop/config.json -->
|
||||
|
||||
```json
|
||||
//# Your configuration here
|
||||
```
|
||||
27
.github/ISSUE_TEMPLATE/Feature_request.md
vendored
Normal file
27
.github/ISSUE_TEMPLATE/Feature_request.md
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
name: "Feature Request"
|
||||
about: "I have a suggestion (and may want to implement it)!"
|
||||
title: '[Feature] '
|
||||
labels: "enhancement"
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
IMPORTANT:
|
||||
If your request is related to a specific package, open the issue in the relevant bucket,
|
||||
not here.
|
||||
By opening this issue you confirm that you have searched for similar issues/PRs here already.
|
||||
Failing to do so will most likely result in closing of this issue without any explanation.
|
||||
Incomplete form details below might also result in closing of the issue.
|
||||
-->
|
||||
|
||||
## Feature Request
|
||||
|
||||
#### Is your feature request related to a problem? Please describe.
|
||||
<!-- A clear and concise description of what the problem is. Ex. I have an issue when [...] -->
|
||||
|
||||
#### Describe the solution you'd like
|
||||
<!-- A clear and concise description of what you want to happen. Add any considered drawbacks. -->
|
||||
|
||||
#### Describe alternatives you've considered
|
||||
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
|
||||
2
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
2
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
blank_issues_enabled: false
|
||||
|
||||
35
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
35
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
<!-- Provide a general summary of your changes in the Title above -->
|
||||
<!-- To help with semantic versioning the PR title should start with one of the conventional commit types. -->
|
||||
<!-- The conventional commit types for Semantic PR are: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert -->
|
||||
|
||||
<!--
|
||||
By opening this PR you confirm that you have searched for similar issues/PRs here already.
|
||||
Failing to do so will most likely result in closing of this PR without any explanation.
|
||||
It is also mandatory to open a relevant issue for discussion with the maintainers,
|
||||
before creating any new PR.
|
||||
Read the contributing guide first to save both your and our time.
|
||||
-->
|
||||
|
||||
#### Description
|
||||
<!-- Describe your changes in detail -->
|
||||
|
||||
#### Motivation and Context
|
||||
<!-- Why is this change required? What problem does it solve? -->
|
||||
<!-- If it fixes an open issue, please link to the issue here. -->
|
||||
Closes #XXXX
|
||||
<!-- or -->
|
||||
Relates to #XXXX
|
||||
|
||||
#### How Has This Been Tested?
|
||||
<!-- Please describe in detail how you tested your changes. -->
|
||||
<!-- Include details of your testing environment, tests ran to see how -->
|
||||
<!-- your change affects other areas of the code, etc. -->
|
||||
|
||||
#### Checklist:
|
||||
<!-- 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.
|
||||
39
.github/workflows/ci.yml
vendored
Normal file
39
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
name: Scoop Core CI Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
test_powershell:
|
||||
name: WindowsPowerShell
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 2
|
||||
- name: Init Test Suite
|
||||
uses: potatoqualitee/psmodulecache@v4
|
||||
with:
|
||||
modules-to-cache: PSScriptAnalyzer, BuildHelpers, Pester:4.10.1
|
||||
shell: powershell
|
||||
- name: Test Scoop Core
|
||||
shell: powershell
|
||||
run: ./test/bin/test.ps1
|
||||
test_pwsh:
|
||||
name: PowerShell
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 2
|
||||
- name: Init Test Suite
|
||||
uses: potatoqualitee/psmodulecache@v4
|
||||
with:
|
||||
modules-to-cache: PSScriptAnalyzer, BuildHelpers, Pester:4.10.1
|
||||
shell: pwsh
|
||||
- name: Test Scoop Core
|
||||
shell: pwsh
|
||||
run: ./test/bin/test.ps1
|
||||
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@@ -1,9 +1,5 @@
|
||||
// Configure PSScriptAnalyzer settings
|
||||
{
|
||||
"[powershell]": {
|
||||
// Disable formating until: https://github.com/PowerShell/vscode-powershell/issues/1019 is fixed
|
||||
"editor.formatOnSave": false
|
||||
},
|
||||
"powershell.scriptAnalysis.settingsPath": "PSScriptAnalyzerSettings.psd1",
|
||||
"powershell.codeFormatting.preset": "OTBS",
|
||||
"powershell.codeFormatting.alignPropertyValuePairs": true,
|
||||
|
||||
662
CHANGELOG.md
Normal file
662
CHANGELOG.md
Normal file
@@ -0,0 +1,662 @@
|
||||
## [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
|
||||
|
||||
- **scoop-bucket:** List more detailed information for buckets ([#4704](https://github.com/ScoopInstaller/Scoop/issues/4704), [#4756](https://github.com/ScoopInstaller/Scoop/issues/4756), [#4759](https://github.com/ScoopInstaller/Scoop/issues/4759))
|
||||
- **scoop-cache:** Handle multiple apps and show detailed information ([#4738](https://github.com/ScoopInstaller/Scoop/issues/4738))
|
||||
- **scoop-cat:** Use `bat` to pretty-print JSON ([#4742](https://github.com/ScoopInstaller/Scoop/issues/4742))
|
||||
- **scoop-config:** Allow Scoop to ignore running processes during reset/uninstall/update ([#4713](https://github.com/ScoopInstaller/Scoop/issues/4713), [#4731](https://github.com/ScoopInstaller/Scoop/issues/4731))
|
||||
- **scoop-config:** Show all settings ([#4765](https://github.com/ScoopInstaller/Scoop/issues/4765))
|
||||
- **scoop-download:** Add `scoop download` command ([#4621](https://github.com/ScoopInstaller/Scoop/issues/4621))
|
||||
- **scoop-(install|virustotal):** Allow skipping update check ([#4634](https://github.com/ScoopInstaller/Scoop/issues/4634))
|
||||
- **scoop-list:** Allow list manipulation ([#4718](https://github.com/ScoopInstaller/Scoop/issues/4718))
|
||||
- **scoop-list:** Show last-updated time ([#4723](https://github.com/ScoopInstaller/Scoop/issues/4723))
|
||||
- **scoop-info:** Revamp details and show more information ([#4747](https://github.com/ScoopInstaller/Scoop/issues/4747))
|
||||
- **scoop-shim:** Add `scoop shim` to manipulate shims ([#4727](https://github.com/ScoopInstaller/Scoop/issues/4727), [#4736](https://github.com/ScoopInstaller/Scoop/issues/4736))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **autoupdate:** Allow checksum file that contains whitespaces ([#4619](https://github.com/ScoopInstaller/Scoop/issues/4619))
|
||||
- **autoupdate:** Rename $response to $res ([#4706](https://github.com/ScoopInstaller/Scoop/issues/4706))
|
||||
- **config:** Ensure manipulating config with UTF8 encoding ([#4644](https://github.com/ScoopInstaller/Scoop/issues/4644))
|
||||
- **config:** Allow scoop config use Unicode characters ([#4631](https://github.com/ScoopInstaller/Scoop/issues/4631))
|
||||
- **config:** Fix `set_config` bugs ([#3681](https://github.com/ScoopInstaller/Scoop/issues/3681))
|
||||
- **current:** Remove 'current' while it's not a junction ([#4687](https://github.com/ScoopInstaller/Scoop/issues/4687))
|
||||
- **depends:** Prevent error on no URL ([#4595](https://github.com/ScoopInstaller/Scoop/issues/4595))
|
||||
- **depends:** Check if extractor is available ([#4042](https://github.com/ScoopInstaller/Scoop/issues/4042))
|
||||
- **decompress:** Fix nested Zstd archive extraction ([#4608](https://github.com/ScoopInstaller/Scoop/issues/4608), [#4639](https://github.com/ScoopInstaller/Scoop/issues/4639))
|
||||
- **installed:** Fix 'core/installed' that mark failed app as 'installed' ([#4650](https://github.com/ScoopInstaller/Scoop/issues/4650), [#4676](https://github.com/ScoopInstaller/Scoop/issues/4676), [#4689](https://github.com/ScoopInstaller/Scoop/issues/4689), [#4785](https://github.com/ScoopInstaller/Scoop/issues/4785))
|
||||
- **no-junctions:** Fix error when `NO_JUNCTIONS` is been set ([#4722](https://github.com/ScoopInstaller/Scoop/issues/4722), [#4726](https://github.com/ScoopInstaller/Scoop/issues/4726))
|
||||
- **shim:** Fix PS1 shim error when in different drive in PS7 ([#4614](https://github.com/ScoopInstaller/Scoop/issues/4614))
|
||||
- **shim:** Fix `sh` shim error in WSL ([#4637](https://github.com/ScoopInstaller/Scoop/issues/4637))
|
||||
- **shim:** Use `-file` instead of `-command` in ps1 script shims ([#4721](https://github.com/ScoopInstaller/Scoop/issues/4721))
|
||||
- **shim:** Fix exe shim when app path has white spaces ([#4734](https://github.com/ScoopInstaller/Scoop/issues/4734), [#4780](https://github.com/ScoopInstaller/Scoop/issues/4780))
|
||||
- **versions:** Fix wrong version number when only one version dir ([#4679](https://github.com/ScoopInstaller/Scoop/issues/4679))
|
||||
- **versions:** Get current version from failed installation if possible ([#4720](https://github.com/ScoopInstaller/Scoop/issues/4720), [#4725](https://github.com/ScoopInstaller/Scoop/issues/4725))
|
||||
- **scoop-alias:** Fix alias initialization ([#4737](https://github.com/ScoopInstaller/Scoop/issues/4737))
|
||||
- **scoop-checkup:** Skip 'check_windows_defender' when have not admin privileges ([#4699](https://github.com/ScoopInstaller/Scoop/issues/4699))
|
||||
- **scoop-cleanup:** Remove apps other than current version ([#4665](https://github.com/ScoopInstaller/Scoop/issues/4665))
|
||||
- **scoop-search:** Remove redundant 'bucket/' in search result ([#4773](https://github.com/ScoopInstaller/Scoop/issues/4773))
|
||||
- **scoop-update:** Skip updating non git buckets ([#4670](https://github.com/ScoopInstaller/Scoop/issues/4670), [#4672](https://github.com/ScoopInstaller/Scoop/issues/4672))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
- **uninstall:** Avoid checking all files for unlinking persisted data ([#4681](https://github.com/ScoopInstaller/Scoop/issues/4681), [#4763](https://github.com/ScoopInstaller/Scoop/issues/4763))
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
- **depends:** Rewrite 'depends.ps1' ([#4638](https://github.com/ScoopInstaller/Scoop/issues/4638), [#4673](https://github.com/ScoopInstaller/Scoop/issues/4673))
|
||||
- **mklink:** Use 'New-Item' instead of 'mklink' ([#4690](https://github.com/ScoopInstaller/Scoop/issues/4690))
|
||||
- **rmdir:** Use 'Remove-Item' instead of 'rmdir' ([#4691](https://github.com/ScoopInstaller/Scoop/issues/4691))
|
||||
- **COMSPEC:** Deprecate use of subshell cmd.exe ([#4692](https://github.com/ScoopInstaller/Scoop/issues/4692))
|
||||
- **git:** Use 'git -C' to specify the work directory instead of 'Push-Location'/'Pop-Location' ([#4697](https://github.com/ScoopInstaller/Scoop/issues/4697))
|
||||
- **scoop-info:** Use List View for output ([#4741](https://github.com/ScoopInstaller/Scoop/issues/4741))
|
||||
- **scoop-config:** Use underscores everywhere ([#4745](https://github.com/ScoopInstaller/Scoop/issues/4745))
|
||||
|
||||
### Builds
|
||||
|
||||
- **checkver:** Fix output with '-Version' ([#3774](https://github.com/ScoopInstaller/Scoop/issues/3774))
|
||||
- **schema:** Add '$schema' property ([#4623](https://github.com/ScoopInstaller/Scoop/issues/4623))
|
||||
- **schema:** Add explicit escape to opening bracket matcher in jp/jsonpath regex ([#3719](https://github.com/ScoopInstaller/Scoop/issues/3719))
|
||||
- **schema:** Fix typo ('note' -> 'notes') ([#4678](https://github.com/ScoopInstaller/Scoop/issues/4678))
|
||||
- **tests:** Support both AppVeyor and GitHub Actions ([#4655](https://github.com/ScoopInstaller/Scoop/issues/4655))
|
||||
- **tests:** Run GitHub Actions CI on each commit ([#4664](https://github.com/ScoopInstaller/Scoop/issues/4664))
|
||||
- **tests:** Use cache in GitHub Actions ([#4671](https://github.com/ScoopInstaller/Scoop/issues/4671))
|
||||
- **tests:** Disable CI test on 'push' ([#4677](https://github.com/ScoopInstaller/Scoop/issues/4677))
|
||||
- **vscode-settings:** Remove 'formatOnSave' trigger ([#4635](https://github.com/ScoopInstaller/Scoop/issues/4635))
|
||||
|
||||
### Styles
|
||||
|
||||
- **test:** Format scripts by VSCode's PowerShell extension ([#4609](https://github.com/ScoopInstaller/Scoop/issues/4609))
|
||||
- **style:** Use correct casing for `$PSScriptRoot` ([#4775](https://github.com/ScoopInstaller/Scoop/issues/4775))
|
||||
|
||||
### Tests
|
||||
|
||||
- **test-bin:** Only write output file in CI and fix trailing whitespaces ([#4613](https://github.com/ScoopInstaller/Scoop/issues/4613))
|
||||
- **manifest:** Fix manifests validation ([#4620](https://github.com/ScoopInstaller/Scoop/issues/4620))
|
||||
- **zstd:** Fix 'zstd' extraction error in test ([#4651](https://github.com/ScoopInstaller/Scoop/issues/4651))
|
||||
|
||||
### Documentation
|
||||
|
||||
- **changelog:** Add 'CHANGLOG.md' ([#4600](https://github.com/ScoopInstaller/Scoop/issues/4600))
|
||||
- **changelog:** Rearrange CHANGELOG ([#4729](https://github.com/ScoopInstaller/Scoop/issues/4729))
|
||||
- **changelog:** Link CHANGELOG headers to 'releases/tag' ([#4730](https://github.com/ScoopInstaller/Scoop/issues/4730))
|
||||
|
||||
## [2021-12-26](https://github.com/ScoopInstaller/Scoop/compare/2021-11-22...2021-12-26)
|
||||
|
||||
### Features
|
||||
|
||||
- **core:** Redirect 'StandardError' in `Invoke-ExternalCommand()` ([#4570](https://github.com/ScoopInstaller/Scoop/issues/4570), [#4582](https://github.com/ScoopInstaller/Scoop/issues/4582))
|
||||
- **install:** Add portableapps.com to strip_filename skips ([#3244](https://github.com/ScoopInstaller/Scoop/issues/3244))
|
||||
- **install:** Show manifest on installation ([#4155](https://github.com/ScoopInstaller/Scoop/issues/4155), [fb496c48](https://github.com/ScoopInstaller/Scoop/commit/fb496c482bec4063e01b328f943224ab703dbbd8), [#4581](https://github.com/ScoopInstaller/Scoop/issues/4581))
|
||||
- **template:** Add issue/PR templates ([#4572](https://github.com/ScoopInstaller/Scoop/issues/4572))
|
||||
- **scoop-cat:** Add `scoop cat` command ([#4532](https://github.com/ScoopInstaller/Scoop/issues/4532))
|
||||
- **scoop-config:** Document all configuration options ([#4579](https://github.com/ScoopInstaller/Scoop/issues/4579))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **bucket:** Remove JetBrains bucket ([dec25980](https://github.com/ScoopInstaller/Scoop/commit/dec25980525a81c176b3fd5f238e964db00f3be3))
|
||||
- **bucket:** Remove nightlies bucket ([48b035d7](https://github.com/ScoopInstaller/Scoop/commit/48b035d7f99baa2e81d87ead4ff03a9594e49c3d))
|
||||
- **core:** Escape '.' in 'parse_app()'. ([#4578](https://github.com/ScoopInstaller/Scoop/issues/4578))
|
||||
- **core:** Use '-Encoding ASCII' in 'Out-File' ([#4571](https://github.com/ScoopInstaller/Scoop/issues/4571))
|
||||
- **depends:** Specify function scope ([4d5fee36](https://github.com/ScoopInstaller/Scoop/commit/4d5fee36e1ed13fc850fd22a5414186aec030c6e))
|
||||
- **install:** Use `Select-CurrentVersion` ([#4535](https://github.com/ScoopInstaller/Scoop/issues/4535))
|
||||
- **install:** 'env_add_path' doesn't append '.' ([#4550](https://github.com/ScoopInstaller/Scoop/issues/4550))
|
||||
- **repo:** Update repo links ([cbe29edd](https://github.com/ScoopInstaller/Scoop/commit/cbe29eddb3475e34740300eb1c2c52715446e3be))
|
||||
- **scoop-update:** Update apps with '--all' ([ac71fccb](https://github.com/ScoopInstaller/Scoop/commit/ac71fccbecb3d4158f249db9c1b9bb043cb8e966))
|
||||
- **scoop-update:** Fix scoop update -a requiring arguments ([#4531](https://github.com/ScoopInstaller/Scoop/issues/4531))
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
- **shim:** Rework shimming logic ([#4543](https://github.com/ScoopInstaller/Scoop/issues/4543), [#4555](https://github.com/ScoopInstaller/Scoop/issues/4555), [3c90d1a0](https://github.com/ScoopInstaller/Scoop/commit/3c90d1a0701b0b64730dbf9ebc8d31f9b9c238f1), [2ec00d57](https://github.com/ScoopInstaller/Scoop/commit/2ec00d576c7e594dc5c0f1eac4536c5310ce6f17))
|
||||
|
||||
### Builds
|
||||
|
||||
- **auto-pr:** Remove hardcoded 'master' branch ([#4567](https://github.com/ScoopInstaller/Scoop/issues/4567))
|
||||
- **checkver:** Improve JSONPath extraction support ([#4522](https://github.com/ScoopInstaller/Scoop/issues/4522))
|
||||
- **checkver:** Use GitHub token from environment ([#4557](https://github.com/ScoopInstaller/Scoop/issues/4557))
|
||||
- **schema:** Enable autoupdate for 'license' ([#4528](https://github.com/ScoopInstaller/Scoop/issues/4528), [#4596](https://github.com/ScoopInstaller/Scoop/issues/4596))
|
||||
|
||||
### Documentation
|
||||
|
||||
- **readme:** Add link to Contributing Guide ([5e11c94a](https://github.com/ScoopInstaller/Scoop/commit/5e11c94a544ff2adbdbec5072c32a94d3e5acb9c))
|
||||
- **readme:** Fix links ([3bb7036e](https://github.com/ScoopInstaller/Scoop/commit/3bb7036ee111bfe58e82ba3d0fd39189b058776a))
|
||||
|
||||
### Reverts
|
||||
|
||||
- **shim:** Revert [#4229](https://github.com/ScoopInstaller/Scoop/issues/4229) ([#4553](https://github.com/ScoopInstaller/Scoop/issues/4553))
|
||||
|
||||
## [2021-11-22](https://github.com/ScoopInstaller/Scoop/compare/2020-11-26...2021-11-22)
|
||||
|
||||
### Features
|
||||
|
||||
- **bucket:** Move extras bucket to [@ScoopInstaller](https://github.com/ScoopInstaller) ([3e9a4d4e](https://github.com/ScoopInstaller/Scoop/commit/3e9a4d4ea0e7e4d6489099c46a763f58db07e633))
|
||||
- **decompress:** Support Zstandard archive ([#4372](https://github.com/ScoopInstaller/Scoop/issues/4372), [e35ff313](https://github.com/ScoopInstaller/Scoop/commit/e35ff313a5d35cab1049024938c3423a5f6bf060), [47ebc6f1](https://github.com/ScoopInstaller/Scoop/commit/47ebc6f176b0db0afeb51b4ee237a20b2d8649e9))
|
||||
- **install:** Handle arch-specific env_add_path ([#4013](https://github.com/ScoopInstaller/Scoop/issues/4013))
|
||||
- **install:** s/lukesamson/ScoopInstaller in install.ps1 ([5226f26f](https://github.com/ScoopInstaller/Scoop/commit/5226f26f18157ed78f1529144404ec682374452e))
|
||||
- **message:** Add config to disable aria2 warning message ([#4422](https://github.com/ScoopInstaller/Scoop/issues/4422))
|
||||
- **shim:** Add another alternative shim written in rust ([#4229](https://github.com/ScoopInstaller/Scoop/issues/4229))
|
||||
- **scoop-prefix:** Remove unused imports and functions ([#4494](https://github.com/ScoopInstaller/Scoop/issues/4494))
|
||||
- **scoop-install:** Auto uninstall previous failed installation ([#3281](https://github.com/ScoopInstaller/Scoop/issues/3281))
|
||||
- **scoop-update:** Add flags `--all` as an alternative to '*' to update all ([#3871](https://github.com/ScoopInstaller/Scoop/issues/3871))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **core:** Change url() scope to avoid conflict with global aliases ([#4342](https://github.com/ScoopInstaller/Scoop/issues/4342), [#4492](https://github.com/ScoopInstaller/Scoop/issues/4492))
|
||||
- **install:** Fix `aria2`'s resume download feature ([#3292](https://github.com/ScoopInstaller/Scoop/issues/3292))
|
||||
- **shim:** Fixed trailing whitespace issue ([#4307](https://github.com/ScoopInstaller/Scoop/issues/4307))
|
||||
- **scoop-reset:** Skip when app instance is running ([#4359](https://github.com/ScoopInstaller/Scoop/issues/4359))
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
- **versions:** Refactor 'versions.ps1' ([#3721](https://github.com/ScoopInstaller/Scoop/issues/3721), [e6630272](https://github.com/ScoopInstaller/Scoop/commit/e663027299d03ca768a252fa4bcbc51d124d4cae), [ae892138](https://github.com/ScoopInstaller/Scoop/commit/ae892138423bb9bbf54c8f0bed8331b93199f6b8))
|
||||
|
||||
### Builds
|
||||
|
||||
- **autoupdate:** Add multiple URL/hash/extract_dir... support ([#3518](https://github.com/ScoopInstaller/Scoop/issues/3518), [#4502](https://github.com/ScoopInstaller/Scoop/issues/4502))
|
||||
- **schema:** Fix Schema to support `+` in version ([#4504](https://github.com/ScoopInstaller/Scoop/issues/4504))
|
||||
- **supporting:** Update Json to 12.0.3, Json.Schema to 3.0.14 ([#3352](https://github.com/ScoopInstaller/Scoop/issues/3352))
|
||||
|
||||
### Documentation
|
||||
|
||||
- **readme:** Capitalize to prevent redirect ([#4483](https://github.com/ScoopInstaller/Scoop/issues/4483))
|
||||
- **readme:** s/lukesampson/ScoopInstaller in readme ([4f5acd72](https://github.com/ScoopInstaller/Scoop/commit/4f5acd72109a98a148d1bfa269c23a2d43644d23))
|
||||
- **readme:** Update extras bucket url in readme ([f1a46e10](https://github.com/ScoopInstaller/Scoop/commit/f1a46e109596c55c7e83c77fc1fc9daedbe71636))
|
||||
- **readme:** Update Java bucket text ([#4514](https://github.com/ScoopInstaller/Scoop/issues/4514))
|
||||
- **readme:** Update notes about the NirSoft bucket ([#4524](https://github.com/ScoopInstaller/Scoop/issues/4524))
|
||||
|
||||
## [2020-11-26](https://github.com/ScoopInstaller/Scoop/compare/2020-10-22...2020-11-26)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **shim:** Fix Makefile typo ([0948824e](https://github.com/ScoopInstaller/Scoop/commit/0948824ec7269c979882d09342d9a269193cd674)) ([227de6cf](https://github.com/ScoopInstaller/Scoop/commit/227de6cfb8433a86ac0f0a279e691327ae04554c))
|
||||
|
||||
## [2020-10-22](https://github.com/ScoopInstaller/Scoop/compare/2019-10-23...2020-10-22)
|
||||
|
||||
### Features
|
||||
|
||||
- **aria2:** Inline progress ([#3987](https://github.com/ScoopInstaller/Scoop/issues/3987))
|
||||
- **autoupdate:** Add $urlNoExt and $basenameNoExt substitutions ([#3742](https://github.com/ScoopInstaller/Scoop/issues/3742))
|
||||
- **config:** Add configuration option for default architecture ([#3778](https://github.com/ScoopInstaller/Scoop/issues/3778))
|
||||
- **install:** Follow HTTP redirections when downloading a file ([#3902](https://github.com/ScoopInstaller/Scoop/issues/3902))
|
||||
- **install:** Let pathes in 'env_add_path' be added ascendantly ([#3788](https://github.com/ScoopInstaller/Scoop/issues/3788), [#3976](https://github.com/ScoopInstaller/Scoop/issues/3976))
|
||||
- **list:** Display main bucket name ([#3759](https://github.com/ScoopInstaller/Scoop/issues/3759))
|
||||
- **shim:** Add alt-shim support ([#3998](https://github.com/ScoopInstaller/Scoop/issues/3998))
|
||||
- **scoop-checkup:** Add check_envs_requirements ([#3860](https://github.com/ScoopInstaller/Scoop/issues/3860))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **bucket:** Update scoop-nonportable URL ([#3776](https://github.com/ScoopInstaller/Scoop/issues/3776))
|
||||
- **download:** Fosshub download ([#4051](https://github.com/ScoopInstaller/Scoop/issues/4051))
|
||||
- **download:** Progress bar on small files ([96de9c14](https://github.com/ScoopInstaller/Scoop/commit/96de9c14bb483f9278e4b0a9e22b1923ee752901))
|
||||
- **hold:** Replace "locked" terminology with "held" for consistency ([#3917](https://github.com/ScoopInstaller/Scoop/issues/3917))
|
||||
- **git:** Don't execute autostart programs when executing git commands ([#3993](https://github.com/ScoopInstaller/Scoop/issues/3993))
|
||||
- **git:** Enforce pull without rebase ([#3765](https://github.com/ScoopInstaller/Scoop/issues/3765))
|
||||
- **install:** Aria2 inline progress negative values ([#4053](https://github.com/ScoopInstaller/Scoop/issues/4053))
|
||||
- **install:** Fix wrong output of 'install/failed' ([#3784](https://github.com/ScoopInstaller/Scoop/issues/3784), [#3867](https://github.com/ScoopInstaller/Scoop/issues/3867))
|
||||
- **install:** Re-add "Don't send referer to portableapps.com" ([#3961](https://github.com/ScoopInstaller/Scoop/issues/3961))
|
||||
- **scoop:** Remove temporary code from the scoop executable ([#3898](https://github.com/ScoopInstaller/Scoop/issues/3898))
|
||||
- **update:** Update outdated PowerShell 5 warning ([#3986](https://github.com/ScoopInstaller/Scoop/issues/3986))
|
||||
- **scoop-info:** Check bucket of installed app ([#3740](https://github.com/ScoopInstaller/Scoop/issues/3740))
|
||||
|
||||
### Builds
|
||||
|
||||
- **checkver:** Present script property ([#3900](https://github.com/ScoopInstaller/Scoop/issues/3900))
|
||||
|
||||
### Tests
|
||||
|
||||
- **init:** Force pester v4 ([#4040](https://github.com/ScoopInstaller/Scoop/issues/4040))
|
||||
|
||||
## [2019-10-23](https://github.com/ScoopInstaller/Scoop/compare/2019-10-18...2019-10-23)
|
||||
|
||||
### Features
|
||||
|
||||
- **update:** Support $persist_dir in uninstaller.script ([#3692](https://github.com/ScoopInstaller/Scoop/issues/3692))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **core:** Use [Environment]::Is64BitOperatingSystem instead of [intptr]::size ([#3690](https://github.com/ScoopInstaller/Scoop/issues/3690))
|
||||
- **git:** Remove unnecessary git_proxy_cmd() calls for local commands ([8ee45a57](https://github.com/ScoopInstaller/Scoop/commit/8ee45a57dc01a525dcf8776bf9bb45263992c81f))
|
||||
- **install:** Check execution policy ([#3619](https://github.com/ScoopInstaller/Scoop/issues/3619))
|
||||
- **update:** Fix scoop update changelog output ([e997017f](https://github.com/ScoopInstaller/Scoop/commit/e997017f1a03e2eefef2157acdfefe2e4fced896))
|
||||
|
||||
## [2019-10-18](https://github.com/ScoopInstaller/Scoop/compare/2019-06-24...2019-10-18)
|
||||
|
||||
### Features
|
||||
|
||||
- **core:** Tweak Invoke-ExternalCommand parameters ([#3547](https://github.com/ScoopInstaller/Scoop/issues/3547))
|
||||
- **install:** Use 7zip when available for faster zip file extraction ([#3460](https://github.com/ScoopInstaller/Scoop/issues/3460))
|
||||
- **install:** Add arch support to `env_add_path` and `env_set` ([#3503](https://github.com/ScoopInstaller/Scoop/issues/3503))
|
||||
- **install:** Allow $version to be used in uninstaller scripts ([#3592](https://github.com/ScoopInstaller/Scoop/issues/3592))
|
||||
- **install:** Allow installing specific version if latest is installed ([11c42d78](https://github.com/ScoopInstaller/Scoop/commit/11c42d782f8adb29fbe0d94daa5f121cdda935ab))
|
||||
- **update:** Allow updating apps from local manifest or URL ([#3685](https://github.com/ScoopInstaller/Scoop/issues/3685))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **autoupdate:** Decode basename when extract hash ([#3615](https://github.com/ScoopInstaller/Scoop/issues/3615))
|
||||
- **autoupdate:** Remove any whitespace from hash ([#3579](https://github.com/ScoopInstaller/Scoop/issues/3579))
|
||||
- **bucket:** Only lookup directories in buckets folder ([#3631](https://github.com/ScoopInstaller/Scoop/issues/3631))
|
||||
- **comspec:** Escape variables when calling COMSPEC commands ([#3538](https://github.com/ScoopInstaller/Scoop/issues/3538))
|
||||
- **decompress:** Fix bugs on extract_dir ([#3540](https://github.com/ScoopInstaller/Scoop/issues/3540))
|
||||
- **editorconfig:** Add missing } to bat/cmd regex ([#3529](https://github.com/ScoopInstaller/Scoop/issues/3529))
|
||||
- **help:** Rename help() to scoop_help() ([#3564](https://github.com/ScoopInstaller/Scoop/issues/3564))
|
||||
- **install:** Use Join-Path instead of string gluing. ([#3566](https://github.com/ScoopInstaller/Scoop/issues/3566))
|
||||
- **scoop-info:** Fix output for single binaries with alias ([#3651](https://github.com/ScoopInstaller/Scoop/issues/3651))
|
||||
- **scoop-info:** Remove a whitespace ([#3652](https://github.com/ScoopInstaller/Scoop/issues/3652))
|
||||
|
||||
### Builds
|
||||
|
||||
- **auto-pr:** Fix git status detection ([7decfd4c](https://github.com/ScoopInstaller/Scoop/commit/7decfd4c107b8d8a59d7eedfe8a56e1801120c2f))
|
||||
- **auto-pr:** Hard reset bucket after running ([79f8538b](https://github.com/ScoopInstaller/Scoop/commit/79f8538b57b9021db71a279879b9032fefd1ae52))
|
||||
- **checkurls:** Trim renaming suffix in url ([#3677](https://github.com/ScoopInstaller/Scoop/issues/3677))
|
||||
|
||||
### Continuous Integration
|
||||
|
||||
- **appveyor:** use VS2019 image to fix PS6 issues ([#3646](https://github.com/ScoopInstaller/Scoop/issues/3646))
|
||||
- **tests:** Do not force maintainers to have SCOOP_HELPERS ([#3604](https://github.com/ScoopInstaller/Scoop/issues/3604))
|
||||
|
||||
### Documentation
|
||||
|
||||
- **readme:** Improve installation instructions ([#3600](https://github.com/ScoopInstaller/Scoop/issues/3600))
|
||||
|
||||
## [2019-06-24](https://github.com/ScoopInstaller/Scoop/compare/2019-05-15...2019-06-24)
|
||||
|
||||
### Features
|
||||
|
||||
- **decompress:** Add 'ExtractDir' to 'Expand-...' functions ([#3466](https://github.com/ScoopInstaller/Scoop/issues/3466), [#3470](https://github.com/ScoopInstaller/Scoop/issues/3470), [#3472](https://github.com/ScoopInstaller/Scoop/issues/3472))
|
||||
- **decompress:** Allow 'Expand-InnoArchive -ExtractDir' to accept '{xxx}' ([#3487](https://github.com/ScoopInstaller/Scoop/issues/3487))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **config:** Show correct output when removing a config value ([#3462](https://github.com/ScoopInstaller/Scoop/issues/3462))
|
||||
- **decompress:** Change dark.exe parameter order ([6141e46d](https://github.com/ScoopInstaller/Scoop/commit/6141e46d6ae74b3ccf65e02a1c3fc92e1b4d3e7a))
|
||||
- **proxy:** Rename parameters for Net.NetworkCredential ([#3483](https://github.com/ScoopInstaller/Scoop/issues/3483))
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
- **core:** `run()` -> 'Invoke-ExternalCommand()' ([#3432](https://github.com/ScoopInstaller/Scoop/issues/3432))
|
||||
|
||||
### Builds
|
||||
|
||||
- **checkhashes:** Checkhashes downloading twice when architecture properties does hot have url property ([#3479](https://github.com/ScoopInstaller/Scoop/issues/3479))
|
||||
- **checkhashes:** Do not call scoop directly ([#3527](https://github.com/ScoopInstaller/Scoop/issues/3527))
|
||||
|
||||
### Documentation
|
||||
|
||||
- **readme:** Add known buckets to end of readme ([2849e0f9](https://github.com/ScoopInstaller/Scoop/commit/2849e0f96099004f761d7d8c715377e0d2c105f2))
|
||||
- **readme:** Adjust URL of `runat.json` ([#3484](https://github.com/ScoopInstaller/Scoop/issues/3484))
|
||||
- **readme:** Fix a small typo ([#3512](https://github.com/ScoopInstaller/Scoop/issues/3512))
|
||||
- **readme:** Fix typo in readme ([03bb07c8](https://github.com/ScoopInstaller/Scoop/commit/03bb07c8231563fa3a2092b9b52d4dde372f2a8e))
|
||||
- **readme:** Update readme with correct count of nirsoft apps ([e8d0be66](https://github.com/ScoopInstaller/Scoop/commit/e8d0be663b3bab25d9ee55c597b90bf922f4ec5d))
|
||||
|
||||
## [2019-05-15](https://github.com/ScoopInstaller/Scoop/compare/2019-05-12...2019-05-15)
|
||||
|
||||
### Features
|
||||
|
||||
- **manifest:** XPath support in checkver and autoupdate ([#3458](https://github.com/ScoopInstaller/Scoop/issues/3458))
|
||||
- **update:** Support changing scoop tracking repository ([#3459](https://github.com/ScoopInstaller/Scoop/issues/3459))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **autoupdate:** Handle xml namespace in xpath mode ([#3465](https://github.com/ScoopInstaller/Scoop/issues/3465))
|
||||
|
||||
## [2019-05-12](https://github.com/ScoopInstaller/Scoop/releases/tag/2019-05-12)
|
||||
|
||||
### BREAKING CHANGE
|
||||
|
||||
- **core:** Finalize bucket extraction ([#3399](https://github.com/ScoopInstaller/Scoop/issues/3399))
|
||||
- **core:** Bucket extraction and refactoring ([#3449](https://github.com/ScoopInstaller/Scoop/issues/3449))
|
||||
|
||||
### Features
|
||||
|
||||
- **autoupdate:** Add 'regex' alias for 'find' ([3453487e](https://github.com/ScoopInstaller/Scoop/commit/3453487ed65378cc9ba2efc658ed6bc1431ef463))
|
||||
- **autoupdate:** Allow simple metalink and meta4 hash extraction ([ecf627c3](https://github.com/ScoopInstaller/Scoop/commit/ecf627c3b8493b3ccf7ddb882b0c946533774d76))
|
||||
- **autoupdate:** Add version variables to autoupdate.hash.regex ([#2966](https://github.com/ScoopInstaller/Scoop/issues/2996))
|
||||
- **autoupdate:** Autoupdate improvements ([#1371](https://github.com/ScoopInstaller/Scoop/issues/1371))
|
||||
- **autoupdate:** Convert base64 encoded hash values ([04c9ddeb](https://github.com/ScoopInstaller/Scoop/commit/04c9ddeb6d3b99496c39543ad468d34f4f1adeff))
|
||||
- **autoupdate:** Improve base64 hash detection ([310096e2](https://github.com/ScoopInstaller/Scoop/commit/310096e2386ff3bf9082d547b140a98b92b87a83))
|
||||
- **core:** Add basic WSL support by appending .exe to powershell ([#2323](https://github.com/ScoopInstaller/Scoop/issues/2323))
|
||||
- **core:** Add Expand-DarkArchive and some other dependency features ([#3450](https://github.com/ScoopInstaller/Scoop/issues/3450))
|
||||
- **core:** Enable TLS 1.2 in core.ps1 ([#2074](https://github.com/ScoopInstaller/Scoop/issues/2074))
|
||||
- **core:** Prepare extraction of main bucket ([#3060](https://github.com/ScoopInstaller/Scoop/issues/3060))
|
||||
- **core:** Support loading basedirs from config ([#3121](https://github.com/ScoopInstaller/Scoop/issues/3121))
|
||||
- **core:** Update requirement to version 5 or greater ([#3330](https://github.com/ScoopInstaller/Scoop/issues/3330))
|
||||
- **core:** Use consistent User-Agent Header on all webrequests ([12962acf](https://github.com/ScoopInstaller/Scoop/commit/12962acfa853593e371d09186e51660aece331e5))
|
||||
- **core:** Warn on shim overwrite ([#2033](https://github.com/ScoopInstaller/Scoop/issues/2033))
|
||||
- **debug:** Add option to indent debug output ([bf54a978](https://github.com/ScoopInstaller/Scoop/commit/bf54a978a1bc2efcde52513a60ec5bcb7bb1a44e))
|
||||
- **decompress:** Allow other args to be passthrough ([#3411](https://github.com/ScoopInstaller/Scoop/issues/3411))
|
||||
- **download:** Add support for multi-connection downloads via aria2c ([#2312](https://github.com/ScoopInstaller/Scoop/issues/2312))
|
||||
- **download:** Convert sourceforge urls to use downloads mirror ([#3340](https://github.com/ScoopInstaller/Scoop/issues/3340))
|
||||
- **install:** Add .NET 4.5 check to scoop install script ([e52c24c9](https://github.com/ScoopInstaller/Scoop/commit/e52c24c94ec805a327440cc07aec699afc7cc308))
|
||||
- **install:** Set file write permission to global persist dir ([#2524](https://github.com/ScoopInstaller/Scoop/issues/2524))
|
||||
- **json:** Normalize multi-line strings to string arrays on format ([#2444](https://github.com/ScoopInstaller/Scoop/issues/2444))
|
||||
- **persist:** Support persisting files without a file extension ([#2408](https://github.com/ScoopInstaller/Scoop/issues/2408))
|
||||
- **shim:** Add '.com'-type shim ([#3366](https://github.com/ScoopInstaller/Scoop/issues/3366))
|
||||
- **shim:** Enabled applications which require elevated privileges ([#2053](https://github.com/ScoopInstaller/Scoop/issues/2053))
|
||||
- **shim:** Enabled shimming of external applications ([#2072](https://github.com/ScoopInstaller/Scoop/issues/2072))
|
||||
- **shim:** Enabled wide characters forwarding in shims ([#2106](https://github.com/ScoopInstaller/Scoop/issues/2106))
|
||||
- **shim:** Make shim support PowerShell 2.0 ([#2562](https://github.com/ScoopInstaller/Scoop/issues/2562))
|
||||
- **shim:** Create sh shim ([#1951](https://github.com/ScoopInstaller/Scoop/issues/1951))
|
||||
- **shortcuts:** Add subdirectories/arguments for shortcuts ([#1945](https://github.com/ScoopInstaller/Scoop/issues/1945))
|
||||
- **shortcuts:** Allow $dir, $original_dir and $persist_dir substitutions for shortcuts ([f3ddf0c0](https://github.com/ScoopInstaller/Scoop/commit/f3ddf0c0f81ee2a11466edf5d9f6e38a0fc2b9d4))
|
||||
- **shortcuts:** Get start menu folder location from environment rather than predefined user profile path ([c245a7fe](https://github.com/ScoopInstaller/Scoop/commit/c245a7fe96ffa0b0fba23bd47f31480ea93cc183))
|
||||
- **uninstall:** Print purge step to console ([#3123](https://github.com/ScoopInstaller/Scoop/issues/3123))
|
||||
- **uninstall:** Add support for soft/purge uninstalling of scoop itself ([#2781](https://github.com/ScoopInstaller/Scoop/issues/2781))
|
||||
- **update:** Add hold/unhold command ([#3444](https://github.com/ScoopInstaller/Scoop/issues/3444))
|
||||
- **scoop-checkup:** Add NTFS check to checkup command ([#1944](https://github.com/ScoopInstaller/Scoop/issues/1944))
|
||||
- **scoop-checkup:** Check for LongPaths setting ([#3387](https://github.com/ScoopInstaller/Scoop/issues/3387))
|
||||
- **scoop-info:** Add scoop-info command ([#2165](https://github.com/ScoopInstaller/Scoop/issues/2165))
|
||||
- **scoop-info:** Support url manifest ([#2538](https://github.com/ScoopInstaller/Scoop/issues/2538))
|
||||
- **scoop-prefix:** Add scoop prefix command ([#2117](https://github.com/ScoopInstaller/Scoop/issues/2117))
|
||||
- **scoop-update:** Add notification for new main bucket ([#3392](https://github.com/ScoopInstaller/Scoop/issues/3392))
|
||||
- **scoop-update:** Show changelog after updating scoop and buckets ([56c35f8f](https://github.com/ScoopInstaller/Scoop/commit/56c35f8f05ed387997ef1a80ec0362adec6e51a5))
|
||||
- **scoop-which:** Also show other applications in PATH with 'scoop which' ([79bf99c3](https://github.com/ScoopInstaller/Scoop/commit/79bf99c3c110494d799e147263db7b6f2f921d4e))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **autoupdate:** Fix base64 hash extraction ([98afb999](https://github.com/ScoopInstaller/Scoop/commit/98afb99990561c4f98f1e1334f348e52b4bee4e7))
|
||||
- **autoupdate:** Fix base64 hash extraction length ([#2852](https://github.com/ScoopInstaller/Scoop/issues/2852))
|
||||
- **autoupdate:** Fix metalink hash extraction ([2ad54747](https://github.com/ScoopInstaller/Scoop/commit/2ad547477b1432e7a269c90b393d62d88dce9803))
|
||||
- **autoupdate:** Fix single line hash extraction ([#3015](https://github.com/ScoopInstaller/Scoop/issues/3015))
|
||||
- **autoupdate:** Improve auto-update hash extraction regex ([21bf0dea](https://github.com/ScoopInstaller/Scoop/commit/21bf0dea561db021aa59bcd9363792436ac7c162))
|
||||
- **autoupdate:** Linter fix ([c9539b65](https://github.com/ScoopInstaller/Scoop/commit/c9539b6575e8842a8f895d82b4119c3aef01d7c2))
|
||||
- **autoupdate:** Use normal variable instead of magic $matches variable name ([d74e0a85](https://github.com/ScoopInstaller/Scoop/commit/d74e0a85b4081745bd1ab107a45794f02299737d))
|
||||
- **bucket:** Change wording of new_issue_msg() ([e82587df](https://github.com/ScoopInstaller/Scoop/commit/e82587dfc41618474e03347df333e847dfaffc70))
|
||||
- **bucket:** Fix new_issue_msg ([#3375](https://github.com/ScoopInstaller/Scoop/issues/3375))
|
||||
- **bucket:** Use $_.Name on gci result ([1eb2609d](https://github.com/ScoopInstaller/Scoop/commit/1eb2609db51587772a4b5d1b6f58114f52639568))
|
||||
- **config:** Enable writing to hidden .scoop file ([#1982](https://github.com/ScoopInstaller/Scoop/issues/1982))
|
||||
- **config:** Save and load true/false values as booleans in scoops config ([16aec1a4](https://github.com/ScoopInstaller/Scoop/commit/16aec1a40b45ba241ef2ac45ccf89de7206891be))
|
||||
- **core:** Allowed underscores in package names ([#2930](https://github.com/ScoopInstaller/Scoop/issues/2930))
|
||||
- **core:** Clean up some error messages ([#2032](https://github.com/ScoopInstaller/Scoop/issues/2032))
|
||||
- **core:** Change sf regex not to break some manifests ([#2476](https://github.com/ScoopInstaller/Scoop/issues/2476))
|
||||
- **core:** Check if 7zip installed via Scoop instead of using 7z.exe from PATH ([55ce0c0b](https://github.com/ScoopInstaller/Scoop/commit/55ce0c0b0c481ec3807655cb7aeac6dfcf9ef271))
|
||||
- **core:** Filter null or empty string from Scoops directory settings ([5d5c7fa9](https://github.com/ScoopInstaller/Scoop/commit/5d5c7fa91c03f05b705d3420618ec96d8e870174))
|
||||
- **core:** Fix "enable-encryptionscheme" for OSes before Windows 10 ([#2084](https://github.com/ScoopInstaller/Scoop/issues/2084))
|
||||
- **core:** Fix bug with Start-Process -Wait, exclusive to PowerShell Core on Windows 7 ([#3415](https://github.com/ScoopInstaller/Scoop/issues/3415))
|
||||
- **core:** Fix for relative paths ([ff9c0c3d](https://github.com/ScoopInstaller/Scoop/commit/ff9c0c3dafb3567ee958379b83205da84a925ecf))
|
||||
- **core:** Fix robocopy not releasing a directory after moving it ([e2792f2e](https://github.com/ScoopInstaller/Scoop/commit/e2792f2e02adee5947ebb95022a62282fb61024f))
|
||||
- **core:** Fix substitute() for arrays ([#2048](https://github.com/ScoopInstaller/Scoop/issues/2048))
|
||||
- **core:** Format hashes to lowercase ([5d56f8ff](https://github.com/ScoopInstaller/Scoop/commit/5d56f8ff5760ddedaf44eaf9652000e833b0944e))
|
||||
- **core:** Invoke powershell with -noprofile flag from bash shims ([#3165](https://github.com/ScoopInstaller/Scoop/issues/3165))
|
||||
- **core:** Removed the bucket from the app name when checking directories ([#2435](https://github.com/ScoopInstaller/Scoop/issues/2435))
|
||||
- **core:** Return true for checking if a directory is 'in' itself ([ac8a1567](https://github.com/ScoopInstaller/Scoop/commit/ac8a156796cb6d3d9cba24a2839271d924ab8fea))
|
||||
- **core:** Store last update time as String ([e8af15cc](https://github.com/ScoopInstaller/Scoop/commit/e8af15cc0615b707aee79be95f9c00e3ae0bfd9b))
|
||||
- **decompress:** Add .bz2 files to decompression ([#2085](https://github.com/ScoopInstaller/Scoop/issues/2085))
|
||||
- **decompress:** Added retry when unzip fail because of AV ([#1822](https://github.com/ScoopInstaller/Scoop/issues/1822))
|
||||
- **decompress:** Catch unzip failures from bad file names ([#2472](https://github.com/ScoopInstaller/Scoop/issues/2472))
|
||||
- **decompress:** Compatible Expand-ZipArchive() with Pscx ([#3425](https://github.com/ScoopInstaller/Scoop/issues/3425))
|
||||
- **decompress:** Correct deprecation function name ([#3406](https://github.com/ScoopInstaller/Scoop/issues/3406))
|
||||
- **decompress:** Fix dark parameter order ([87a1e784](https://github.com/ScoopInstaller/Scoop/commit/87a1e784d7463fea36fa41fcb7cb5537cbcfdc52))
|
||||
- **depends:** Don't force adding dark dependency ([#3453](https://github.com/ScoopInstaller/Scoop/issues/3453))
|
||||
- **depends:** Don't include the requested app in the list of dependencies ([1bc6a479](https://github.com/ScoopInstaller/Scoop/commit/1bc6a479ee969e44e2b0d83ed6ff19efd86c6ae9))
|
||||
- **depends:** Fix empty bucket name ([#2827](https://github.com/ScoopInstaller/Scoop/issues/2827))
|
||||
- **depends:** Fix null reference error when no buckets are configured ([88040972](https://github.com/ScoopInstaller/Scoop/commit/88040972a30b459a3859c7c2f883e47e19da9f84))
|
||||
- **depends:** Show message about missing bucket when installing dependencies ([7a6218c5](https://github.com/ScoopInstaller/Scoop/commit/7a6218c58677170fe32cf1c2bfcfe7488e4c3655))
|
||||
- **download:** Don't send referer to portableapps.com ([0c2b3da3](https://github.com/ScoopInstaller/Scoop/commit/0c2b3da3ff639722ad3ddf587219bb3155e97c7f))
|
||||
- **download:** Fix fosshub downloads with aria2c ([803525a8](https://github.com/ScoopInstaller/Scoop/commit/803525a8661ffaa39fc4ad6f0dc776cccad4c45e))
|
||||
- **download:** Interrupt download causes partial cache file to be used for next install ([5be02865](https://github.com/ScoopInstaller/Scoop/commit/5be0286561398debfee2c0610e51f006ef2dc2fb))
|
||||
- **download:** Overwrite any existing files when extracting ([58cca68f](https://github.com/ScoopInstaller/Scoop/commit/58cca68f7565bd5e8f63e08ad052c0029b98a23d))
|
||||
- **download:** Show warning about SourceForge.net hash validation fails ([8504338b](https://github.com/ScoopInstaller/Scoop/commit/8504338bc5faab3235cef2e1c2f41abd7ae496eb))
|
||||
- **getopt:** Don't try to parse int arguments ([23fe5a53](https://github.com/ScoopInstaller/Scoop/commit/23fe5a5319d4ede84c532df04f576c3854fd5826))
|
||||
- **getopt:** Don't try to parse array arguments ([9b6e7b5e](https://github.com/ScoopInstaller/Scoop/commit/9b6e7b5e0f7f6ddecdb139f932ad7d582fe639a4))
|
||||
- **getopt:** Return remaining args, use getopt for scoop install ([b7cfd6fd](https://github.com/ScoopInstaller/Scoop/commit/b7cfd6fdb0e18a623ceacfa6fc824241dabc6d01))
|
||||
- **getopt:** Skip if arg is $null ([f2d9f0d7](https://github.com/ScoopInstaller/Scoop/commit/f2d9f0d79fdf4a63879c1b87a6c0f5317a40a1d9))
|
||||
- **getopt:** Skip arg if it's decimal ([5f0c8cfb](https://github.com/ScoopInstaller/Scoop/commit/5f0c8cfb0a34078bb8118a21191cf046ddad18ac))
|
||||
- **git:** Disable git pager when running git log ([cac99759](https://github.com/ScoopInstaller/Scoop/commit/cac9975924691fe6e608789218b06be56bb8c658))
|
||||
- **git:** Fix update log output ([0daa25c6](https://github.com/ScoopInstaller/Scoop/commit/0daa25c6300cd2ab605d63b71037d741c9c904c6))
|
||||
- **json:** Catch JsonReaderException ([fb58e92c](https://github.com/ScoopInstaller/Scoop/commit/fb58e92c13552199f19f5df112801fc41321eee2))
|
||||
- **install:** Add filename to warning for files without hash in the manifest ([4c9beee8](https://github.com/ScoopInstaller/Scoop/commit/4c9beee8f2df891b2ec314e1efffb2ee9d5cca20))
|
||||
- **install:** Add multi-line support to pre/post_install ([#1980](https://github.com/ScoopInstaller/Scoop/issues/1980))
|
||||
- **install:** Added exclusion for sourceforge. [#METR-21516] ([#2109](https://github.com/ScoopInstaller/Scoop/issues/2109))
|
||||
- **install:** Ignore url fragment for PowerShell Core 6.1.0 ([#2602](https://github.com/ScoopInstaller/Scoop/issues/2602))
|
||||
- **install:** Fix fail when installing from non-default bucket ([#2247](https://github.com/ScoopInstaller/Scoop/issues/2247))
|
||||
- **install:** Fix PowerShell core crash ([#2554](https://github.com/ScoopInstaller/Scoop/issues/2554))
|
||||
- **install:** Option to skip hash validation and error message improvements ([#2260](https://github.com/ScoopInstaller/Scoop/issues/2260))
|
||||
- **install:** Remove env_ensure_home ([#1967](https://github.com/ScoopInstaller/Scoop/issues/1967))
|
||||
- **install:** Show first 8 bytes of file in the hash check error message ([e4cbb42e](https://github.com/ScoopInstaller/Scoop/commit/e4cbb42e64843e53b5b24de92f43062bca98c474))
|
||||
- **persist:** Fix condition for persist_permission() ([eb7b7cbf](https://github.com/ScoopInstaller/Scoop/commit/eb7b7cbf4f30e4122762856723155f3c1e980d1b)) ([1a2598bc](https://github.com/ScoopInstaller/Scoop/commit/1a2598bc3082a2e3fffac1a6bea0b42032e388f0))
|
||||
- **persist:** Fixed persisting bug when force update app with same version ([#2774](https://github.com/ScoopInstaller/Scoop/issues/2774))
|
||||
- **persist:** Fix the target didn't be created ([#3008](https://github.com/ScoopInstaller/Scoop/issues/3008))
|
||||
- **persist:** Prevent directory creation from being output ([#1999](https://github.com/ScoopInstaller/Scoop/issues/1999))
|
||||
- **scoop:** Force to add new main bucket ([#3419](https://github.com/ScoopInstaller/Scoop/issues/3419))
|
||||
- **shim:** Fix .ps1 shim parsing logic ([#2564](https://github.com/ScoopInstaller/Scoop/issues/2564))
|
||||
- **shim:** Fixed ps1/jar->ps1 shims args handling ([#2120](https://github.com/ScoopInstaller/Scoop/issues/2120))
|
||||
- **shortcuts:** Improve Shortcut creation ([83b82386](https://github.com/ScoopInstaller/Scoop/commit/83b823868f5ef5256d3dcfbecff278bb355fefc8))
|
||||
- **uninstall:** Better error handling during uninstallation ([#2079](https://github.com/ScoopInstaller/Scoop/issues/2079))
|
||||
- **uninstall:** Uninstall fails to remove architecture-specific shims ([8b1871b2](https://github.com/ScoopInstaller/Scoop/commit/8b1871b20df4dbf1b603d4066937ba213c03bb32))
|
||||
- **update:** Rewording PowerShell update notice ([d006fb93](https://github.com/ScoopInstaller/Scoop/commit/d006fb9315b55a9d8e6a36218cf5dbdde51433ec))
|
||||
- **versions:** Improvements for the reset command to deal with empty current alias dir correctly ([#2896](https://github.com/ScoopInstaller/Scoop/issues/2896))
|
||||
- **scoop-alias:** Improve "scoop alias list" output ([#2163](https://github.com/ScoopInstaller/Scoop/issues/2163))
|
||||
- **scoop-cache:** Display help on incorrect cache command ([#3431](https://github.com/ScoopInstaller/Scoop/issues/3431))
|
||||
- **scoop-cache:** scoop cache command not using $SCOOP_CACHE ([#1990](https://github.com/ScoopInstaller/Scoop/issues/1990))
|
||||
- **scoop-info:** Improve scoop-info license attributes output ([#2397](https://github.com/ScoopInstaller/Scoop/issues/2397))
|
||||
- **scoop-install:** Prevent installing programs from JSON multiple times ([936cf9cb](https://github.com/ScoopInstaller/Scoop/commit/936cf9cbb0c4dd3a594fbaf5c696ce519e586d8c))
|
||||
- **scoop-reset:** Persist data on reset ([#2773](https://github.com/ScoopInstaller/Scoop/issues/2773))
|
||||
- **scoop-reset:** Re-create shortcuts ([6e5b7e57](https://github.com/ScoopInstaller/Scoop/commit/6e5b7e57bb0628f072872d9a5b8c8a0fa58389e1))
|
||||
- **scoop-search:** Better handling for invalid query ([bf024705](https://github.com/ScoopInstaller/Scoop/commit/bf024705a8cc38592571aa3026dca2471f19ac5a))
|
||||
- **scoop-uninstall:** Checked if uninstaller removed its directory ([#2078](https://github.com/ScoopInstaller/Scoop/issues/2078))
|
||||
- **scoop-update:** Add config option "show_update_log" ([d68cb3ce](https://github.com/ScoopInstaller/Scoop/commit/d68cb3ce52acaa9983f278822febd506f54ebe02))
|
||||
- **scoop-update:** First scoop update fails because scoop deletes itself too early ([376630fd](https://github.com/ScoopInstaller/Scoop/commit/376630fd80a3f9012fd6e673460b9e28e375e951))
|
||||
- **scoop-update:** Fix branch switching ([#3372](https://github.com/ScoopInstaller/Scoop/issues/3372))
|
||||
- **scoop-update:** Fix update with cookies ([#3261](https://github.com/ScoopInstaller/Scoop/issues/3261))
|
||||
- **scoop-update:** Improve is_scoop_outdated() and add last_scoop_update() ([f3f559c4](https://github.com/ScoopInstaller/Scoop/commit/f3f559c460406689dab2375310fb1026e2be58bd))
|
||||
- **scoop-update:** Resolve linting, fix appveyor tests error ([#2148](https://github.com/ScoopInstaller/Scoop/issues/2148))
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
- **bucket:** Move function into lib from lib-exec ([#3062](https://github.com/ScoopInstaller/Scoop/issues/3062))
|
||||
- **bucket:** Optimize buckets function ([#3341](https://github.com/ScoopInstaller/Scoop/issues/3341))
|
||||
- **config:** Move configuration handling to core.ps1 ([#3242](https://github.com/ScoopInstaller/Scoop/issues/3242))
|
||||
- **core:** aria2_path() -> file_path() ([0f464016](https://github.com/ScoopInstaller/Scoop/commit/0f4640168da8d68a52eb2b80af2f3ffa01c9b658))
|
||||
- **core:** cmd_available() -> Test-CommandAvailable() ([#3314](https://github.com/ScoopInstaller/Scoop/issues/3314))
|
||||
- **core:** ensure_all_installed() -> Confirm-InstallationStatus() ([#3293](https://github.com/ScoopInstaller/Scoop/issues/3293))
|
||||
- **core:** Move default_aliases into the scoped function ([#3233](https://github.com/ScoopInstaller/Scoop/issues/3233))
|
||||
- **core:** Refactor function names and fix installing 7zip locally if already globally available ([#3416](https://github.com/ScoopInstaller/Scoop/issues/3416))
|
||||
- **core:** Simplified last_scoop_update() ([#2931](https://github.com/ScoopInstaller/Scoop/issues/2931))
|
||||
- **core:** Tweak SecurityProtocol usage ([#3065](https://github.com/ScoopInstaller/Scoop/issues/3065))
|
||||
- **decompress:** Refactored (w/ install.ps1, core.ps1) ([#3169](https://github.com/ScoopInstaller/Scoop/issues/3169))
|
||||
- **decompress:** Refactor extraction handling functions ([#3204](https://github.com/ScoopInstaller/Scoop/issues/3204))
|
||||
- **download:** Download functionality refactor ([#1329](https://github.com/ScoopInstaller/Scoop/issues/1329))
|
||||
- **install:** Rename locate() to Find-Manifest() ([9eed3d89](https://github.com/ScoopInstaller/Scoop/commit/9eed3d8914c7a0fa294110eb0761776a01adf034))
|
||||
|
||||
### Builds
|
||||
|
||||
- **auto-pr:** Add -App parameter ([#3157](https://github.com/ScoopInstaller/Scoop/issues/3157))
|
||||
- **auto-pr:** Add SkipUpdated parameter ([#3168](https://github.com/ScoopInstaller/Scoop/issues/3168))
|
||||
- **checkhashes:** Add bin\checkhashes.ps1 ([#2766](https://github.com/ScoopInstaller/Scoop/issues/2766))
|
||||
- **checkurls:** Add SkipValid Parameter ([#2845](https://github.com/ScoopInstaller/Scoop/issues/2845))
|
||||
- **checkurls:** Import config.ps1 in checkurls.ps1 ([126e9c97](https://github.com/ScoopInstaller/Scoop/commit/126e9c97d2ef7db537a5137167089a97f343e98e))
|
||||
- **checkver:** Add 'useragent' property ([8feb3867](https://github.com/ScoopInstaller/Scoop/commit/8feb3867a74ea0340585e3e695934d96cf483a05))
|
||||
- **checkver:** Add 'jsonpath' alias for 'jp' ([76fdb6b7](https://github.com/ScoopInstaller/Scoop/commit/76fdb6b74c1772bf607d2dad5f6c50269369ff88))
|
||||
- **checkver:** Add 're' alias 'regex' ([468649c8](https://github.com/ScoopInstaller/Scoop/commit/468649c88dea9c1ff9614f2cdf29a521d572664e))
|
||||
- **checkver:** Allow using the current version in checkver URL ([607ac9ca](https://github.com/ScoopInstaller/Scoop/commit/607ac9ca7c185da61e2c746ea87d28c2abe62adc))
|
||||
- **checkver:** Fix example parameters ([#3413](https://github.com/ScoopInstaller/Scoop/issues/3413))
|
||||
- **checkver:** GitHub checkver case-insensitive version check ([2e2633e9](https://github.com/ScoopInstaller/Scoop/commit/2e2633e9640f6cab5c2f895b680345cd6ca))
|
||||
- **checkver:** Remove old commented code ([72754036](https://github.com/ScoopInstaller/Scoop/commit/72754036a251fffd2f2eb0e242edfd9895543e3c))
|
||||
- **checkver:** Resolve issue on Powershell >6.1.0 ([#2592](https://github.com/ScoopInstaller/Scoop/issues/2592))
|
||||
- **checkver:** Support skipping up to date manifests ([#2624](https://github.com/ScoopInstaller/Scoop/issues/2624))
|
||||
- **schema:** Add shortcutsArray definition to schema.json ([0c7e6002](https://github.com/ScoopInstaller/Scoop/commit/0c7e60024a06e122331b17a204a158e4c5800a3d))
|
||||
- **schema:** extract_to property is on active duty (not deprecated) ([59e994c5](https://github.com/ScoopInstaller/Scoop/commit/59e994c5fdeb8dffe6037ca6767d56ad13bf04da))
|
||||
- **schema:** Improve comments in schema.json ([b5ed0761](https://github.com/ScoopInstaller/Scoop/commit/b5ed0761aef4f3e864533dc0460d110115850ba7))
|
||||
- **supporting:** Update validator.exe and shim.exe ([#2024](https://github.com/ScoopInstaller/Scoop/issues/2024), [#2034](https://github.com/ScoopInstaller/Scoop/issues/2034))
|
||||
- **supporting:** Update Newtonsoft.Json to 11.0.2, Newtonsoft.Json.Schema to 3.0.10 ([#3043](https://github.com/ScoopInstaller/Scoop/issues/3043))
|
||||
- **validator:** Improve error reporting, add support for multiple files ([#3134](https://github.com/ScoopInstaller/Scoop/issues/3134))
|
||||
|
||||
### Continuous Integration
|
||||
|
||||
- **appveyor:** Rebuild cache ([7311b41b](https://github.com/ScoopInstaller/Scoop/commit/7311b41b8d1e2e010175fb7d079662bbcba5bac8))
|
||||
- **appveyor:** Run tests for PowerShell 5 and 6 ([#2603](https://github.com/ScoopInstaller/Scoop/issues/2603))
|
||||
- **test:** Improve installation of lessmsi and innounp ([#3409](https://github.com/ScoopInstaller/Scoop/issues/3409))
|
||||
|
||||
### Styles
|
||||
|
||||
- **lint:** PSAvoidUsingCmdletAliases ([#2075](https://github.com/ScoopInstaller/Scoop/issues/2075))
|
||||
|
||||
### Tests
|
||||
|
||||
- **bucket:** Add importable tests for Buckets ([478f52c4](https://github.com/ScoopInstaller/Scoop/commit/478f52c421ca35ea35b5fd0b2df2631cf7d82487))
|
||||
- **bucket:** Fix manifest tests for buckets ([589303fa](https://github.com/ScoopInstaller/Scoop/commit/589303facc5284f6f95c1305191e0558c0169691))
|
||||
- **bucket:** Handle JSON.NET schema validation limit exceeded. ([139813a8](https://github.com/ScoopInstaller/Scoop/commit/139813a8f50ace85e2752d9b6c9f82fc64ff3e48))
|
||||
- **file:** Move style constraints tests to separate test file ([7b7113fc](https://github.com/ScoopInstaller/Scoop/commit/7b7113fc3bf962aaeba625f58341c30a80f0fe6a))
|
||||
- **linux:** Fix some tests on linux ([#2153](https://github.com/ScoopInstaller/Scoop/issues/2153))
|
||||
- **manifest:** Expose bucketdir variable in manifest test script ([#2182](https://github.com/ScoopInstaller/Scoop/issues/2182))
|
||||
- **test:** Add -TestPath param to test.ps1 ([f857dce9](https://github.com/ScoopInstaller/Scoop/commit/f857dce9f59a490f6dd07085c3abaa51e9577fda))
|
||||
- **test:** Force install PSScriptAnalyzer and BuildHelpers ([7a1b5a18](https://github.com/ScoopInstaller/Scoop/commit/7a1b5a1840e30321951fa0f5333c34d10f57fa94))
|
||||
- **test:** Require BuildHelpers version 2.0.0 ([ac3ee766](https://github.com/ScoopInstaller/Scoop/commit/ac3ee766722e99c1f15dc60a1f1dfb0a48428c55))
|
||||
- **test:** Update BuildHelpers to version 2.0.1 ([dde4d0f9](https://github.com/ScoopInstaller/Scoop/commit/dde4d0f93f260191af5524c0ecab927f3e252361))
|
||||
- **core:** Use Pester 4.0 syntax in core tests ([#2712](https://github.com/ScoopInstaller/Scoop/issues/2712))
|
||||
- **install:** Use Pester 4.0 syntax to the install tests ([#2713](https://github.com/ScoopInstaller/Scoop/issues/2713))
|
||||
- **test:** Use Pester 4.0 syntax to multiple files ([#2714](https://github.com/ScoopInstaller/Scoop/issues/2714))
|
||||
|
||||
### Documentation
|
||||
|
||||
- **readme:** Add discord chat badge ([#3241](https://github.com/ScoopInstaller/Scoop/issues/3241))
|
||||
- **readme:** Add more details about scoops installation ([#2273](https://github.com/ScoopInstaller/Scoop/issues/2273))
|
||||
- **readme:** Corrected enable powershell executionpolicy ([#2020](https://github.com/ScoopInstaller/Scoop/issues/2020))
|
||||
- **readme:** Update Discord invite link ([5f269249](https://github.com/ScoopInstaller/Scoop/commit/5f269249609b43f5c4fa9aba4def999e7ee05fe1))
|
||||
- **readme:** Update requirements note ([#2509](https://github.com/ScoopInstaller/Scoop/issues/2509))
|
||||
- **readme:** Fix typo (you -> your), (it's -> its) ([#2698](https://github.com/ScoopInstaller/Scoop/issues/2698))
|
||||
- **readme:** Remove trailing whitespaces ([d25186bf](https://github.com/ScoopInstaller/Scoop/commit/d25186bf1f833e30d8c5b530b7c260fe399b75ed))
|
||||
- **readme:** Remove "tail" from example (is coreutils) ([#2158](https://github.com/ScoopInstaller/Scoop/issues/2158))
|
||||
|
||||
## *Commits before 2018 are trimmed*
|
||||
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.
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
# Only diagnostic records of the specified severity will be generated.
|
||||
# Uncomment the following line if you only want Errors and Warnings but
|
||||
# not Information diagnostic records.
|
||||
Severity = @('Error','Warning')
|
||||
Severity = @('Error')
|
||||
|
||||
# Analyze **only** the following rules. Use IncludeRules when you want
|
||||
# to invoke only a small subset of the defualt rules.
|
||||
|
||||
91
README.md
91
README.md
@@ -3,23 +3,23 @@
|
||||
<h1 align="center">Scoop</h1>
|
||||
</p>
|
||||
<p align="center">
|
||||
<b><a href="https://github.com/lukesampson/scoop#what-does-scoop-do">Features</a></b>
|
||||
<b><a href="https://github.com/ScoopInstaller/Scoop#what-does-scoop-do">Features</a></b>
|
||||
|
|
||||
<b><a href="https://github.com/lukesampson/scoop#installation">Installation</a></b>
|
||||
<b><a href="https://github.com/ScoopInstaller/Scoop#installation">Installation</a></b>
|
||||
|
|
||||
<b><a href="https://github.com/lukesampson/scoop/wiki">Documentation</a></b>
|
||||
<b><a href="https://github.com/ScoopInstaller/Scoop/wiki">Documentation</a></b>
|
||||
</p>
|
||||
|
||||
- - -
|
||||
<p align="center" >
|
||||
<a href="https://github.com/lukesampson/scoop">
|
||||
<img src="https://img.shields.io/github/languages/code-size/lukesampson/scoop.svg" alt="Code Size" />
|
||||
<a href="https://github.com/ScoopInstaller/Scoop">
|
||||
<img src="https://img.shields.io/github/languages/code-size/ScoopInstaller/Scoop.svg" alt="Code Size" />
|
||||
</a>
|
||||
<a href="https://github.com/lukesampson/scoop">
|
||||
<img src="https://img.shields.io/github/repo-size/lukesampson/scoop.svg" alt="Repository size" />
|
||||
<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/lukesampson/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" />
|
||||
@@ -27,8 +27,8 @@
|
||||
<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/lukesampson/scoop/blob/master/LICENSE">
|
||||
<img src="https://img.shields.io/github/license/lukesampson/scoop.svg" alt="License" />
|
||||
<a href="./LICENSE">
|
||||
<img src="https://img.shields.io/badge/license-UNLICENSE%20or%20MIT-blue" alt="License" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
@@ -36,14 +36,14 @@ Scoop is a command-line installer for Windows.
|
||||
|
||||
## What does Scoop do?
|
||||
|
||||
Scoop installs programs from the command line with a minimal amount of friction. It tries to eliminate things like:
|
||||
Scoop installs programs from the command line with a minimal amount of friction. It:
|
||||
|
||||
- Permission popup windows
|
||||
- GUI wizard-style installers
|
||||
- Path pollution from installing lots of programs
|
||||
- Unexpected side-effects from installing and uninstalling programs
|
||||
- The need to find and install dependencies
|
||||
- The need to perform extra setup steps to get a working program
|
||||
- Eliminates permission popup windows
|
||||
- Hides GUI wizard-style installers
|
||||
- Prevents PATH pollution from installing lots of programs
|
||||
- Avoids unexpected side-effects from installing and uninstalling programs
|
||||
- Finds and installs dependencies automatically
|
||||
- Performs all the extra setup steps itself to get a working program
|
||||
|
||||
Scoop is very scriptable, so you can run repeatable setups to get your environment just the way you like, e.g.:
|
||||
|
||||
@@ -56,46 +56,17 @@ 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.
|
||||
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.
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
## [Documentation](https://github.com/lukesampson/scoop/wiki)
|
||||
## [Documentation](https://github.com/ScoopInstaller/Scoop/wiki)
|
||||
|
||||
## Multi-connection downloads with `aria2`
|
||||
|
||||
@@ -105,13 +76,17 @@ Scoop can utilize [`aria2`](https://github.com/aria2/aria2) to use multi-connect
|
||||
scoop install aria2
|
||||
```
|
||||
|
||||
By default, `scoop` displays a warning when running `scoop install` or `scoop update` while `aria2` is enabled. This warning can be suppressed by running `scoop config aria2-warning-enabled false`.
|
||||
|
||||
You can tweak the following `aria2` settings with the `scoop config` command:
|
||||
|
||||
- aria2-enabled (default: true)
|
||||
- aria2-warning-enabled (default: true)
|
||||
- [aria2-retry-wait](https://aria2.github.io/manual/en/html/aria2c.html#cmdoption-retry-wait) (default: 2)
|
||||
- [aria2-split](https://aria2.github.io/manual/en/html/aria2c.html#cmdoption-s) (default: 5)
|
||||
- [aria2-max-connection-per-server](https://aria2.github.io/manual/en/html/aria2c.html#cmdoption-x) (default: 5)
|
||||
- [aria2-min-split-size](https://aria2.github.io/manual/en/html/aria2c.html#cmdoption-k) (default: 5M)
|
||||
- [aria2-options](https://aria2.github.io/manual/en/html/aria2c.html#options) (default: )
|
||||
|
||||
## Inspiration
|
||||
|
||||
@@ -126,6 +101,10 @@ Since installers are common, Scoop supports them too (and their uninstallers).
|
||||
|
||||
Scoop is also great at handling single-file programs and Powershell scripts. These don't even need to be compressed. See the [runat](https://github.com/ScoopInstaller/Main/blob/master/bucket/runat.json) package for an example: it's really just a GitHub gist.
|
||||
|
||||
### Contribute to this project
|
||||
|
||||
If you'd like to improve Scoop by adding features or fixing bugs, please read our [Contributing Guide](https://github.com/ScoopInstaller/.github/blob/main/.github/CONTRIBUTING.md).
|
||||
|
||||
### Support this project
|
||||
|
||||
If you find Scoop useful and would like to support ongoing development and maintenance, here's how:
|
||||
@@ -137,14 +116,12 @@ If you find Scoop useful and would like to support ongoing development and maint
|
||||
The following buckets are known to scoop:
|
||||
|
||||
- [main](https://github.com/ScoopInstaller/Main) - Default bucket for the most common (mostly CLI) apps
|
||||
- [extras](https://github.com/lukesampson/scoop-extras) - Apps that don't fit the main bucket's [criteria](https://github.com/lukesampson/scoop/wiki/Criteria-for-including-apps-in-the-main-bucket)
|
||||
- [extras](https://github.com/ScoopInstaller/Extras) - Apps that don't fit the main bucket's [criteria](https://github.com/ScoopInstaller/Scoop/wiki/Criteria-for-including-apps-in-the-main-bucket)
|
||||
- [games](https://github.com/Calinou/scoop-games) - Open source/freeware games and game-related tools
|
||||
- [nerd-fonts](https://github.com/matthewjberger/scoop-nerd-fonts) - Nerd Fonts
|
||||
- [nirsoft](https://github.com/kodybrown/scoop-nirsoft) - A subset of the [250](https://github.com/rasa/scoop-directory/blob/master/by-score.md#MCOfficer_scoop-nirsoft) [Nirsoft](https://nirsoft.net) apps
|
||||
- [java](https://github.com/ScoopInstaller/Java) - Installers for Oracle Java, OpenJDK, Zulu, ojdkbuild, AdoptOpenJDK, Amazon Corretto, BellSoft Liberica & SapMachine
|
||||
- [jetbrains](https://github.com/Ash258/Scoop-JetBrains) - Installers for all JetBrains utilities and IDEs
|
||||
<!-- * [nightlies](https://github.com/ScoopInstaller/Nightlies) - No longer used -->
|
||||
- [nonportable](https://github.com/oltolm/scoop-nonportable) - Non-portable apps (may require UAC)
|
||||
- [nirsoft](https://github.com/kodybrown/scoop-nirsoft) - Almost all of the [250+](https://rasa.github.io/scoop-directory/by-apps#kodybrown_scoop-nirsoft) apps from [Nirsoft](https://nirsoft.net)
|
||||
- [java](https://github.com/ScoopInstaller/Java) - A collection of Java development kits (JDKs), Java runtime engines (JREs), Java's virtual machine debugging tools and Java based runtime engines.
|
||||
- [nonportable](https://github.com/ScoopInstaller/Nonportable) - Non-portable apps (may require UAC)
|
||||
- [php](https://github.com/ScoopInstaller/PHP) - Installers for most versions of PHP
|
||||
- [versions](https://github.com/ScoopInstaller/Versions) - Alternative versions of apps found in other buckets
|
||||
|
||||
@@ -159,4 +136,4 @@ scoop bucket add extras
|
||||
|
||||
## Other application buckets
|
||||
|
||||
Many other application buckets hosted on Github can be found in the [Scoop Directory](https://github.com/rasa/scoop-directory).
|
||||
Many other application buckets hosted on Github can be found in the [Scoop Directory](https://rasa.github.io/scoop-directory/) or via [other search engines](https://rasa.github.io/scoop-directory/#other-search-engines).
|
||||
|
||||
34
appveyor.yml
34
appveyor.yml
@@ -2,36 +2,34 @@ version: "{build}-{branch}"
|
||||
branches:
|
||||
except:
|
||||
- gh-pages
|
||||
build: off
|
||||
deploy: off
|
||||
clone_depth: 49
|
||||
image: Visual Studio 2019
|
||||
build: false
|
||||
deploy: false
|
||||
clone_depth: 2
|
||||
image: Visual Studio 2022
|
||||
environment:
|
||||
scoop: C:\projects\scoop
|
||||
scoop_home: C:\projects\scoop
|
||||
scoop_helpers: C:\projects\helpers
|
||||
lessmsi: '%scoop_helpers%\lessmsi\lessmsi.exe'
|
||||
innounp: '%scoop_helpers%\innounp\innounp.exe'
|
||||
matrix:
|
||||
- PowerShell: 5
|
||||
- PowerShell: 6
|
||||
cache:
|
||||
- '%USERPROFILE%\Documents\WindowsPowerShell\Modules -> appveyor.yml, test\bin\*.ps1'
|
||||
- C:\projects\helpers -> appveyor.yml, test\bin\*.ps1
|
||||
- PowerShell: 7
|
||||
matrix:
|
||||
fast_finish: true
|
||||
for:
|
||||
- matrix:
|
||||
only:
|
||||
- PowerShell: 5
|
||||
cache:
|
||||
- '%USERPROFILE%\Documents\WindowsPowerShell\Modules -> appveyor.yml, test\bin\*.ps1'
|
||||
- C:\projects\helpers -> appveyor.yml, test\bin\*.ps1
|
||||
install:
|
||||
- ps: . "$env:SCOOP_HOME\test\bin\init.ps1"
|
||||
- ps: .\test\bin\init.ps1
|
||||
test_script:
|
||||
- ps: . "$env:SCOOP_HOME\test\bin\test.ps1" -TestPath "$env:APPVEYOR_BUILD_FOLDER"
|
||||
- ps: .\test\bin\test.ps1
|
||||
- matrix:
|
||||
only:
|
||||
- PowerShell: 6
|
||||
- PowerShell: 7
|
||||
cache:
|
||||
- '%USERPROFILE%\Documents\PowerShell\Modules -> appveyor.yml, test\bin\*.ps1'
|
||||
- C:\projects\helpers -> appveyor.yml, test\bin\*.ps1
|
||||
install:
|
||||
- pwsh: . "$env:SCOOP_HOME\test\bin\init.ps1"
|
||||
- pwsh: .\test\bin\init.ps1
|
||||
test_script:
|
||||
- pwsh: . "$env:SCOOP_HOME\test\bin\test.ps1" -TestPath "$env:APPVEYOR_BUILD_FOLDER"
|
||||
- pwsh: .\test\bin\test.ps1
|
||||
|
||||
@@ -2,25 +2,29 @@
|
||||
.SYNOPSIS
|
||||
Updates manifests and pushes them or creates pull-requests.
|
||||
.DESCRIPTION
|
||||
Updates manifests and pushes them directly to the master branch or creates pull-requests for upstream.
|
||||
Updates manifests and pushes them directly to the origin branch or creates pull-requests for upstream.
|
||||
.PARAMETER Upstream
|
||||
Upstream repository with the target branch.
|
||||
Must be in format '<user>/<repo>:<branch>'
|
||||
.PARAMETER OriginBranch
|
||||
Origin (local) branch name.
|
||||
.PARAMETER App
|
||||
Manifest name to search.
|
||||
Placeholders are supported.
|
||||
.PARAMETER Dir
|
||||
The directory where to search for manifests.
|
||||
.PARAMETER Push
|
||||
Push updates directly to 'origin master'.
|
||||
Push updates directly to 'origin branch'.
|
||||
.PARAMETER Request
|
||||
Create pull-requests on 'upstream master' for each update.
|
||||
Create pull-requests on 'upstream branch' for each update.
|
||||
.PARAMETER Help
|
||||
Print help to console.
|
||||
.PARAMETER SpecialSnowflakes
|
||||
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
|
||||
@@ -37,8 +41,8 @@ param(
|
||||
$true
|
||||
})]
|
||||
[String] $Upstream,
|
||||
[String] $OriginBranch = 'master',
|
||||
[String] $App = '*',
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateScript( {
|
||||
if (!(Test-Path $_ -Type Container)) {
|
||||
throw "$_ is not a directory!"
|
||||
@@ -51,26 +55,33 @@ 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 = Resolve-Path $Dir
|
||||
} else {
|
||||
throw "'-Dir' parameter required if '-App' is not a filepath!"
|
||||
}
|
||||
|
||||
if ((!$Push -and !$Request) -or $Help) {
|
||||
Write-Host @'
|
||||
Usage: auto-pr.ps1 [OPTION]
|
||||
|
||||
Mandatory options:
|
||||
-p, -push push updates directly to 'origin master'
|
||||
-r, -request create pull-requests on 'upstream master' for each update
|
||||
-p, -push push updates directly to 'origin branch'
|
||||
-r, -request create pull-requests on 'upstream branch' for each update
|
||||
|
||||
Optional options:
|
||||
-u, -upstream upstream repository with target branch
|
||||
only used if -r is set (default: lukesampson/scoop:master)
|
||||
-o, -originbranch origin (local) branch name
|
||||
-h, -help
|
||||
'@
|
||||
exit 0
|
||||
@@ -90,7 +101,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)"
|
||||
@@ -104,7 +115,7 @@ function pull_requests($json, [String] $app, [String] $upstream, [String] $manif
|
||||
$homepage = $json.homepage
|
||||
$branch = "manifest/$app-$version"
|
||||
|
||||
execute 'hub checkout master'
|
||||
execute "hub checkout $OriginBranch"
|
||||
Write-Host "hub rev-parse --verify $branch" -ForegroundColor Green
|
||||
hub rev-parse --verify $branch
|
||||
|
||||
@@ -141,7 +152,7 @@ a new version of [$app]($homepage) is available.
|
||||
| New version | $version |
|
||||
"@
|
||||
|
||||
hub pull-request -m "$msg" -b '$upstream' -h '$branch'
|
||||
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')"
|
||||
@@ -150,18 +161,18 @@ a new version of [$app]($homepage) is available.
|
||||
|
||||
Write-Host 'Updating ...' -ForegroundColor DarkCyan
|
||||
if ($Push) {
|
||||
execute 'hub pull origin master'
|
||||
execute 'hub checkout master'
|
||||
execute "hub pull origin $OriginBranch"
|
||||
execute "hub checkout $OriginBranch"
|
||||
} else {
|
||||
execute 'hub pull upstream master'
|
||||
execute 'hub push origin master'
|
||||
execute "hub pull upstream $OriginBranch"
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,10 +209,10 @@ hub diff --name-only | ForEach-Object {
|
||||
|
||||
if ($Push) {
|
||||
Write-Host 'Pushing updates ...' -ForegroundColor DarkCyan
|
||||
execute 'hub push origin master'
|
||||
execute "hub push origin $OriginBranch"
|
||||
} else {
|
||||
Write-Host 'Returning to master branch and removing unstaged files ...' -ForegroundColor DarkCyan
|
||||
execute 'hub checkout -f master'
|
||||
Write-Host "Returning to $OriginBranch branch and removing unstaged files ..." -ForegroundColor DarkCyan
|
||||
execute "hub checkout -f $OriginBranch"
|
||||
}
|
||||
|
||||
execute 'hub reset --hard'
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
Manifests without mismatch will not be shown.
|
||||
.PARAMETER UseCache
|
||||
Downloaded files will not be deleted after script finish.
|
||||
Should not be used, because check should be used for downloading actual version of file (as normal user, not finding in some document from vendors, which could be damaged / wrong (Example: Slack@3.3.1 lukesampson/scoop-extras#1192)), not some previously downloaded.
|
||||
Should not be used, because check should be used for downloading actual version of file (as normal user, not finding in some document from vendors, which could be damaged / wrong (Example: Slack@3.3.1 ScoopInstaller/Extras#1192)), not some previously downloaded.
|
||||
.EXAMPLE
|
||||
PS BUCKETROOT> .\bin\checkhashes.ps1
|
||||
Check all manifests for hash mismatch.
|
||||
@@ -75,9 +75,9 @@ foreach ($single in Get-ChildItem $Dir "$App.json") {
|
||||
$manifest.hash | ForEach-Object { $hashes += $_ }
|
||||
} elseif ($manifest.architecture) {
|
||||
# First handle 64bit
|
||||
url $manifest '64bit' | ForEach-Object { $urls += $_ }
|
||||
script:url $manifest '64bit' | ForEach-Object { $urls += $_ }
|
||||
hash $manifest '64bit' | ForEach-Object { $hashes += $_ }
|
||||
url $manifest '32bit' | ForEach-Object { $urls += $_ }
|
||||
script:url $manifest '32bit' | ForEach-Object { $urls += $_ }
|
||||
hash $manifest '32bit' | ForEach-Object { $hashes += $_ }
|
||||
} else {
|
||||
err $name 'Manifest does not contain URL property.'
|
||||
|
||||
@@ -89,8 +89,8 @@ foreach ($man in $Queue) {
|
||||
if ($manifest.url) {
|
||||
$manifest.url | ForEach-Object { $urls += $_ }
|
||||
} else {
|
||||
url $manifest '64bit' | ForEach-Object { $urls += $_ }
|
||||
url $manifest '32bit' | ForEach-Object { $urls += $_ }
|
||||
script:url $manifest '64bit' | ForEach-Object { $urls += $_ }
|
||||
script:url $manifest '32bit' | ForEach-Object { $urls += $_ }
|
||||
}
|
||||
|
||||
$urls | ForEach-Object {
|
||||
|
||||
251
bin/checkver.ps1
251
bin/checkver.ps1
@@ -15,6 +15,10 @@
|
||||
Useful for hash updates.
|
||||
.PARAMETER SkipUpdated
|
||||
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.
|
||||
@@ -48,7 +52,6 @@
|
||||
#>
|
||||
param(
|
||||
[String] $App = '*',
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateScript( {
|
||||
if (!(Test-Path $_ -Type Container)) {
|
||||
throw "$_ is not a directory!"
|
||||
@@ -60,25 +63,40 @@ param(
|
||||
[Switch] $Update,
|
||||
[Switch] $ForceUpdate,
|
||||
[Switch] $SkipUpdated,
|
||||
[String] $Version = ''
|
||||
[String] $Version = '',
|
||||
[Switch] $ThrowError
|
||||
)
|
||||
|
||||
. "$psscriptroot\..\lib\core.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"
|
||||
. "$PSScriptRoot\..\lib\core.ps1"
|
||||
. "$PSScriptRoot\..\lib\autoupdate.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1"
|
||||
. "$PSScriptRoot\..\lib\buckets.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
|
||||
if ($App -ne '*' -and (Test-Path $App -PathType Leaf)) {
|
||||
$Dir = Split-Path $App
|
||||
$files = Get-ChildItem $Dir (Split-Path $App -Leaf)
|
||||
} elseif ($Dir) {
|
||||
$Dir = Resolve-Path $Dir
|
||||
$files = Get-ChildItem $Dir "$App.json"
|
||||
} 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 '') {
|
||||
throw "Don't use '-Version' with '-App *'!"
|
||||
}
|
||||
|
||||
# get apps to check
|
||||
$Queue = @()
|
||||
$json = ''
|
||||
Get-ChildItem $Dir "$App.json" | ForEach-Object {
|
||||
$files | ForEach-Object {
|
||||
$json = parse_json "$Dir\$($_.Name)"
|
||||
if ($json.checkver) {
|
||||
$Queue += , @($_.Name, $json)
|
||||
@@ -86,15 +104,14 @@ Get-ChildItem $Dir "$App.json" | ForEach-Object {
|
||||
}
|
||||
|
||||
# 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 = $_
|
||||
|
||||
$substitutions = get_version_substitutions $json.version
|
||||
$substitutions = Get-VersionSubstitution $json.version # 'autoupdate.ps1'
|
||||
|
||||
$wc = New-Object Net.Webclient
|
||||
if ($json.checkver.useragent) {
|
||||
@@ -102,7 +119,7 @@ $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.]+)'
|
||||
|
||||
@@ -114,18 +131,21 @@ $Queue | ForEach-Object {
|
||||
$jsonpath = ''
|
||||
$xpath = ''
|
||||
$replace = ''
|
||||
$useGithubAPI = $false
|
||||
|
||||
if ($json.checkver -eq 'github') {
|
||||
if (!$json.homepage.StartsWith('https://github.com/')) {
|
||||
error "$name checkver expects the homepage to be a github repository"
|
||||
}
|
||||
$url = $json.homepage + '/releases/latest'
|
||||
$url = $json.homepage.TrimEnd('/') + '/releases/latest'
|
||||
$regex = $githubRegex
|
||||
$useGithubAPI = $true
|
||||
}
|
||||
|
||||
if ($json.checkver.github) {
|
||||
$url = $json.checkver.github + '/releases/latest'
|
||||
$url = $json.checkver.github.TrimEnd('/') + '/releases/latest'
|
||||
$regex = $githubRegex
|
||||
if ($json.checkver.PSObject.Properties.Count -eq 1) { $useGithubAPI = $true }
|
||||
}
|
||||
|
||||
if ($json.checkver.re) {
|
||||
@@ -155,6 +175,13 @@ $Queue | ForEach-Object {
|
||||
|
||||
$reverse = $json.checkver.reverse -and $json.checkver.reverse -eq 'true'
|
||||
|
||||
if ($url -like '*api.github.com/*') { $useGithubAPI = $true }
|
||||
|
||||
if ($useGithubAPI -and ($null -ne $GitHubToken)) {
|
||||
$url = $url -replace '//(www\.)?github.com/', '//api.github.com/repos/'
|
||||
$wc.Headers.Add('Authorization', "token $GitHubToken")
|
||||
}
|
||||
|
||||
$url = substitute $url $substitutions
|
||||
|
||||
$state = New-Object psobject @{
|
||||
@@ -169,7 +196,7 @@ $Queue | ForEach-Object {
|
||||
}
|
||||
|
||||
$wc.Headers.Add('Referer', (strip_filename $url))
|
||||
$wc.DownloadStringAsync($url, $state)
|
||||
$wc.DownloadDataAsync($url, $state)
|
||||
}
|
||||
|
||||
function next($er) {
|
||||
@@ -191,89 +218,102 @@ while ($in_progress -gt 0) {
|
||||
$regexp = $state.regex
|
||||
$jsonpath = $state.jsonpath
|
||||
$xpath = $state.xpath
|
||||
$script = $json.checkver.script
|
||||
$reverse = $state.reverse
|
||||
$replace = $state.replace
|
||||
$expected_ver = $json.version
|
||||
$ver = ''
|
||||
|
||||
$err = $ev.SourceEventArgs.Error
|
||||
$page = $ev.SourceEventArgs.Result
|
||||
|
||||
if ($err) {
|
||||
next "$($err.message)`r`nURL $url is not valid"
|
||||
continue
|
||||
}
|
||||
|
||||
if (!$regex -and $replace) {
|
||||
next "'replace' requires 're' or 'regex'"
|
||||
continue
|
||||
}
|
||||
|
||||
if ($jsonpath) {
|
||||
$ver = json_path $page $jsonpath
|
||||
if (!$ver) {
|
||||
$ver = json_path_legacy $page $jsonpath
|
||||
}
|
||||
if (!$ver) {
|
||||
next "couldn't find '$jsonpath' in $url"
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if ($xpath) {
|
||||
$xml = [xml]$page
|
||||
# Find all `significant namespace declarations` from the XML file
|
||||
$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)
|
||||
}
|
||||
# Getting version from XML, using XPath
|
||||
$ver = $xml.SelectSingleNode($xpath, $nsmgr).'#text'
|
||||
if (!$ver) {
|
||||
next "couldn't find '$xpath' in $url"
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if ($jsonpath -and $regexp) {
|
||||
$page = $ver
|
||||
$ver = ''
|
||||
}
|
||||
|
||||
if ($xpath -and $regexp) {
|
||||
$page = $ver
|
||||
$ver = ''
|
||||
}
|
||||
|
||||
if ($regexp) {
|
||||
$regex = New-Object System.Text.RegularExpressions.Regex($regexp)
|
||||
if ($reverse) {
|
||||
$match = $regex.Matches($page) | Select-Object -Last 1
|
||||
} else {
|
||||
$match = $regex.Matches($page) | Select-Object -First 1
|
||||
}
|
||||
|
||||
if ($match -and $match.Success) {
|
||||
$matchesHashtable = @{}
|
||||
$regex.GetGroupNames() | ForEach-Object { $matchesHashtable.Add($_, $match.Groups[$_].Value) }
|
||||
$ver = $matchesHashtable['1']
|
||||
if ($replace) {
|
||||
$ver = $regex.Replace($match.Value, $replace)
|
||||
}
|
||||
if (!$ver) {
|
||||
$ver = $matchesHashtable['version']
|
||||
}
|
||||
} else {
|
||||
next "couldn't match '$regexp' in $url"
|
||||
continue
|
||||
}
|
||||
}
|
||||
$ver = $Version
|
||||
|
||||
if (!$ver) {
|
||||
next "couldn't find new version in $url"
|
||||
continue
|
||||
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 ($url) {
|
||||
$page = (Get-Encoding($wc)).GetString($ev.SourceEventArgs.Result)
|
||||
}
|
||||
if ($script) {
|
||||
$page = Invoke-Command ([scriptblock]::Create($script -join "`r`n"))
|
||||
}
|
||||
|
||||
if ($jsonpath) {
|
||||
# Return only a single value if regex is absent
|
||||
$noregex = [String]::IsNullOrEmpty($regex)
|
||||
# If reverse is ON and regex is ON,
|
||||
# Then reverse would have no effect because regex handles reverse
|
||||
# on its own
|
||||
# So in this case we have to disable reverse
|
||||
$ver = json_path $page $jsonpath $null ($reverse -and $noregex) $noregex
|
||||
if (!$ver) {
|
||||
$ver = json_path_legacy $page $jsonpath
|
||||
}
|
||||
if (!$ver) {
|
||||
next "couldn't find '$jsonpath' in $url"
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if ($xpath) {
|
||||
$xml = [xml]$page
|
||||
# Find all `significant namespace declarations` from the XML file
|
||||
$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)
|
||||
}
|
||||
# Getting version from XML, using XPath
|
||||
$ver = $xml.SelectSingleNode($xpath, $nsmgr).'#text'
|
||||
if (!$ver) {
|
||||
next "couldn't find '$xpath' in $url"
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if ($jsonpath -and $regexp) {
|
||||
$page = $ver
|
||||
$ver = ''
|
||||
}
|
||||
|
||||
if ($xpath -and $regexp) {
|
||||
$page = $ver
|
||||
$ver = ''
|
||||
}
|
||||
|
||||
if ($regexp) {
|
||||
$regex = New-Object System.Text.RegularExpressions.Regex($regexp)
|
||||
if ($reverse) {
|
||||
$match = $regex.Matches($page) | Select-Object -Last 1
|
||||
} else {
|
||||
$match = $regex.Matches($page) | Select-Object -First 1
|
||||
}
|
||||
|
||||
if ($match -and $match.Success) {
|
||||
$matchesHashtable = @{}
|
||||
$regex.GetGroupNames() | ForEach-Object { $matchesHashtable.Add($_, $match.Groups[$_].Value) }
|
||||
$ver = $matchesHashtable['1']
|
||||
if ($replace) {
|
||||
$ver = $regex.Replace($match.Value, $replace)
|
||||
}
|
||||
if (!$ver) {
|
||||
$ver = $matchesHashtable['version']
|
||||
}
|
||||
} else {
|
||||
next "couldn't match '$regexp' in $url"
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if (!$ver) {
|
||||
next "couldn't find new version in $url"
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
# Skip actual only if versions are same and there is no -f
|
||||
@@ -289,7 +329,7 @@ while ($in_progress -gt 0) {
|
||||
|
||||
Write-Host $ver -ForegroundColor DarkRed -NoNewline
|
||||
Write-Host " (scoop version is $expected_ver)" -NoNewline
|
||||
$update_available = (compare_versions $expected_ver $ver) -eq -1
|
||||
$update_available = (Compare-Version -ReferenceVersion $ver -DifferenceVersion $expected_ver) -ne 0
|
||||
|
||||
if ($json.autoupdate -and $update_available) {
|
||||
Write-Host ' autoupdate available' -ForegroundColor Cyan
|
||||
@@ -305,12 +345,13 @@ while ($in_progress -gt 0) {
|
||||
Write-Host 'Forcing autoupdate!' -ForegroundColor DarkMagenta
|
||||
}
|
||||
try {
|
||||
if ($Version -ne "") {
|
||||
$ver = $Version
|
||||
}
|
||||
autoupdate $App $Dir $json $ver $matchesHashtable
|
||||
Invoke-AutoUpdate $App $Dir $json $ver $matchesHashtable # 'autoupdate.ps1'
|
||||
} catch {
|
||||
error $_.Exception.Message
|
||||
if ($ThrowError) {
|
||||
throw $_
|
||||
} else {
|
||||
error $_.Exception.Message
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -5,21 +5,22 @@
|
||||
$old_erroractionpreference = $erroractionpreference
|
||||
$erroractionpreference = 'stop' # quit if anything goes wrong
|
||||
|
||||
if(($PSVersionTable.PSVersion.Major) -lt 5) {
|
||||
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:
|
||||
if((Get-ExecutionPolicy) -gt 'RemoteSigned' -or (Get-ExecutionPolicy) -eq 'ByPass') {
|
||||
Write-Output "PowerShell requires an execution policy of 'RemoteSigned' to run Scoop."
|
||||
Write-Output "To make this change please run:"
|
||||
$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') {
|
||||
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"
|
||||
@@ -27,27 +28,27 @@ if([System.Enum]::GetNames([System.Net.SecurityProtocolType]) -notcontains 'Tls1
|
||||
}
|
||||
|
||||
# get core functions
|
||||
$core_url = 'https://raw.githubusercontent.com/lukesampson/scoop/master/lib/core.ps1'
|
||||
$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(installed 'scoop') {
|
||||
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 }
|
||||
if ($myinvocation.mycommand.commandtype -eq 'Script') { return } else { exit 1 }
|
||||
}
|
||||
$dir = ensure (versiondir 'scoop' 'current')
|
||||
|
||||
# download scoop zip
|
||||
$zipurl = 'https://github.com/lukesampson/scoop/archive/master.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")
|
||||
[IO.Compression.ZipFile]::ExtractToDirectory($zipfile, "$dir\_tmp")
|
||||
Copy-Item "$dir\_tmp\*master\*" $dir -Recurse -Force
|
||||
Remove-Item "$dir\_tmp", $zipfile -Recurse -Force
|
||||
|
||||
@@ -68,7 +69,6 @@ Copy-Item "$dir\_tmp\*-master\*" $dir -Recurse -Force
|
||||
Remove-Item "$dir\_tmp", $zipfile -Recurse -Force
|
||||
|
||||
ensure_robocopy_in_path
|
||||
ensure_scoop_in_path
|
||||
|
||||
scoop config lastupdate ([System.DateTime]::Now.ToString('o'))
|
||||
success 'Scoop was installed successfully!'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# for development, update the installed scripts to match local source
|
||||
. "$psscriptroot\..\lib\core.ps1"
|
||||
. "$PSScriptRoot\..\lib\core.ps1"
|
||||
|
||||
$src = relpath ".."
|
||||
$src = "$PSScriptRoot\.."
|
||||
$dest = ensure (versiondir 'scoop' 'current')
|
||||
|
||||
# make sure not running from the installed directory
|
||||
@@ -15,5 +15,4 @@ $output | Where-Object { $_ -ne "" }
|
||||
Write-Output 'creating shim...'
|
||||
shim "$dest\bin\scoop.ps1" $false
|
||||
|
||||
ensure_scoop_in_path
|
||||
success 'scoop was refreshed!'
|
||||
|
||||
@@ -1,41 +1,53 @@
|
||||
#requires -v 3
|
||||
param($cmd)
|
||||
#Requires -Version 5
|
||||
Set-StrictMode -Off
|
||||
|
||||
set-strictmode -off
|
||||
. "$PSScriptRoot\..\lib\core.ps1"
|
||||
. "$PSScriptRoot\..\lib\buckets.ps1"
|
||||
. "$PSScriptRoot\..\lib\commands.ps1"
|
||||
. "$PSScriptRoot\..\lib\help.ps1"
|
||||
|
||||
. "$psscriptroot\..\lib\core.ps1"
|
||||
. "$psscriptroot\..\lib\git.ps1"
|
||||
. "$psscriptroot\..\lib\buckets.ps1"
|
||||
. (relpath '..\lib\commands')
|
||||
$subCommand = $Args[0]
|
||||
|
||||
reset_aliases
|
||||
|
||||
# TODO: remove this in a few weeks
|
||||
if ((Get-LocalBucket) -notcontains 'main') {
|
||||
warn "The main bucket of Scoop has been separated to 'https://github.com/ScoopInstaller/Main'"
|
||||
warn "You don't have the main bucket added, adding main bucket for you..."
|
||||
add_bucket 'main'
|
||||
exit
|
||||
# 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
|
||||
}
|
||||
|
||||
$commands = commands
|
||||
if ('--version' -contains $cmd -or (!$cmd -and '-v' -contains $args)) {
|
||||
Push-Location $(versiondir 'scoop' 'current')
|
||||
write-host "Current Scoop version:"
|
||||
git_log --oneline HEAD -n 1
|
||||
write-host ""
|
||||
Pop-Location
|
||||
|
||||
Get-LocalBucket | ForEach-Object {
|
||||
Push-Location (Find-BucketDirectory $_ -Root)
|
||||
if(test-path '.git') {
|
||||
write-host "'$_' bucket:"
|
||||
git_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-CommandAvailable git) -and (Test-Path "$PSScriptRoot\..\.git") -and (get_config SCOOP_BRANCH 'master') -ne 'master') {
|
||||
git -C "$PSScriptRoot\.." --no-pager log --oneline HEAD -n 1
|
||||
} 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)"
|
||||
}
|
||||
Pop-Location
|
||||
Write-Host ''
|
||||
|
||||
Get-LocalBucket | ForEach-Object {
|
||||
$bucketLoc = Find-BucketDirectory $_ -Root
|
||||
if ((Test-Path "$bucketLoc\.git") -and (Test-CommandAvailable git)) {
|
||||
Write-Host "'$_' bucket:"
|
||||
git -C "$bucketLoc" --no-pager log --oneline HEAD -n 1
|
||||
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 }
|
||||
|
||||
@@ -1 +1 @@
|
||||
invoke-pester "$psscriptroot\..\test"
|
||||
. "$PSScriptRoot\..\test\bin\test.ps1"
|
||||
|
||||
@@ -34,7 +34,7 @@ $errors = $false
|
||||
|
||||
# Uninstall given app
|
||||
function do_uninstall($app, $global) {
|
||||
$version = current_version $app $global
|
||||
$version = Select-CurrentVersion -AppName $app -Global:$global
|
||||
$dir = versiondir $app $version $global
|
||||
$manifest = installed_manifest $app $version $global
|
||||
$install = install_info $app $version $global
|
||||
@@ -42,15 +42,15 @@ function do_uninstall($app, $global) {
|
||||
|
||||
Write-Output "Uninstalling '$app'"
|
||||
run_uninstaller $manifest $architecture $dir
|
||||
rm_shims $manifest $global $architecture
|
||||
rm_shims $app $manifest $global $architecture
|
||||
|
||||
# If a junction was used during install, that will have been used
|
||||
# as the reference directory. Othewise it will just be the version
|
||||
# directory.
|
||||
$refdir = unlink_current (appdir $app $global)
|
||||
|
||||
env_rm_path $manifest $refdir $global
|
||||
env_rm $manifest $global
|
||||
env_rm_path $manifest $refdir $global $architecture
|
||||
env_rm $manifest $global $architecture
|
||||
|
||||
$appdir = appdir $app $global
|
||||
try {
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
{
|
||||
"main": "https://github.com/ScoopInstaller/Main",
|
||||
"extras": "https://github.com/lukesampson/scoop-extras",
|
||||
"extras": "https://github.com/ScoopInstaller/Extras",
|
||||
"versions": "https://github.com/ScoopInstaller/Versions",
|
||||
"nightlies": "https://github.com/ScoopInstaller/Nightlies",
|
||||
"nirsoft": "https://github.com/kodybrown/scoop-nirsoft",
|
||||
"php": "https://github.com/ScoopInstaller/PHP",
|
||||
"nerd-fonts": "https://github.com/matthewjberger/scoop-nerd-fonts",
|
||||
"nonportable": "https://github.com/oltolm/scoop-nonportable",
|
||||
"nonportable": "https://github.com/ScoopInstaller/Nonportable",
|
||||
"java": "https://github.com/ScoopInstaller/Java",
|
||||
"games": "https://github.com/Calinou/scoop-games",
|
||||
"jetbrains": "https://github.com/Ash258/Scoop-JetBrains"
|
||||
"games": "https://github.com/Calinou/scoop-games"
|
||||
}
|
||||
|
||||
@@ -1,20 +1,13 @@
|
||||
<#
|
||||
TODO
|
||||
- clean up
|
||||
#>
|
||||
. "$psscriptroot\..\lib\json.ps1"
|
||||
|
||||
. "$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)
|
||||
$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"
|
||||
@@ -22,7 +15,7 @@ function find_hash_in_rdf([String] $url, [String] $basename) {
|
||||
}
|
||||
|
||||
# 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
|
||||
}
|
||||
@@ -43,7 +36,8 @@ function find_hash_in_textfile([String] $url, [Hashtable] $substitutions, [Strin
|
||||
$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"
|
||||
@@ -51,7 +45,7 @@ function find_hash_in_textfile([String] $url, [Hashtable] $substitutions, [Strin
|
||||
}
|
||||
|
||||
if ($regex.Length -eq 0) {
|
||||
$regex = '^([a-fA-F0-9]+)$'
|
||||
$regex = '^\s*([a-fA-F0-9]+)\s*$'
|
||||
}
|
||||
|
||||
$regex = substitute $regex $templates $false
|
||||
@@ -96,7 +90,8 @@ 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)
|
||||
$data = $wc.DownloadData($url)
|
||||
$json = (Get-Encoding($wc)).GetString($data)
|
||||
} catch [system.net.webexception] {
|
||||
write-host -f darkred $_
|
||||
write-host -f darkred "URL $url is not valid"
|
||||
@@ -116,7 +111,8 @@ 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"
|
||||
@@ -152,7 +148,7 @@ function find_hash_in_headers([String] $url) {
|
||||
$req.Timeout = 2000
|
||||
$req.Method = 'HEAD'
|
||||
$res = $req.GetResponse()
|
||||
if(([int]$response.StatusCode -ge 300) -and ([int]$response.StatusCode -lt 400)) {
|
||||
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
|
||||
@@ -178,6 +174,9 @@ function get_hash_for_app([String] $app, $config, [String] $version, [String] $u
|
||||
$substitutions.Add('$url', (strip_fragment $url))
|
||||
$substitutions.Add('$baseurl', (strip_filename (strip_fragment $url)).TrimEnd('/'))
|
||||
$substitutions.Add('$basename', $basename)
|
||||
$substitutions.Add('$urlNoExt', (strip_ext (strip_fragment $url)))
|
||||
$substitutions.Add('$basenameNoExt', (strip_ext $basename))
|
||||
|
||||
debug $substitutions
|
||||
|
||||
$hashfile_url = substitute $config.url $substitutions
|
||||
@@ -281,66 +280,131 @@ function get_hash_for_app([String] $app, $config, [String] $version, [String] $u
|
||||
return $hash
|
||||
}
|
||||
|
||||
function update_manifest_with_new_version($json, [String] $version, [String] $url, [String] $hash, $architecture = $null) {
|
||||
$json.version = $version
|
||||
|
||||
if ($null -eq $architecture) {
|
||||
if ($json.url -is [System.Array]) {
|
||||
$json.url[0] = $url
|
||||
$json.hash[0] = $hash
|
||||
} else {
|
||||
$json.url = $url
|
||||
$json.hash = $hash
|
||||
}
|
||||
} else {
|
||||
# If there are multiple urls we replace the first one
|
||||
if ($json.architecture.$architecture.url -is [System.Array]) {
|
||||
$json.architecture.$architecture.url[0] = $url
|
||||
$json.architecture.$architecture.hash[0] = $hash
|
||||
} else {
|
||||
$json.architecture.$architecture.url = $url
|
||||
$json.architecture.$architecture.hash = $hash
|
||||
}
|
||||
function Update-ManifestProperty {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Update propert(y|ies) in manifest
|
||||
.DESCRIPTION
|
||||
Update selected propert(y|ies) to given version in manifest.
|
||||
.PARAMETER Manifest
|
||||
Manifest to be updated
|
||||
.PARAMETER Property
|
||||
Selected propert(y|ies) to be updated
|
||||
.PARAMETER AppName
|
||||
Software name
|
||||
.PARAMETER Version
|
||||
Given software version
|
||||
.PARAMETER Substitutions
|
||||
Hashtable of internal substitutable variables
|
||||
.OUTPUTS
|
||||
System.Boolean
|
||||
Flag that indicate if there are any changed properties
|
||||
#>
|
||||
[CmdletBinding(SupportsShouldProcess = $true)]
|
||||
[OutputType([Boolean])]
|
||||
param (
|
||||
[Parameter(Mandatory = $true, Position = 1)]
|
||||
[PSCustomObject]
|
||||
$Manifest,
|
||||
[Parameter(ValueFromPipeline = $true, Position = 2)]
|
||||
[String[]]
|
||||
$Property,
|
||||
[String]
|
||||
$AppName,
|
||||
[String]
|
||||
$Version,
|
||||
[Alias('Matches')]
|
||||
[HashTable]
|
||||
$Substitutions
|
||||
)
|
||||
begin {
|
||||
$hasManifestChanged = $false
|
||||
}
|
||||
}
|
||||
|
||||
function update_manifest_prop([String] $prop, $json, [Hashtable] $substitutions) {
|
||||
# first try the global property
|
||||
if ($json.$prop -and $json.autoupdate.$prop) {
|
||||
$json.$prop = substitute $json.autoupdate.$prop $substitutions
|
||||
}
|
||||
|
||||
# check if there are architecture specific variants
|
||||
if ($json.architecture -and $json.autoupdate.architecture) {
|
||||
$json.architecture | Get-Member -MemberType NoteProperty | ForEach-Object {
|
||||
$architecture = $_.Name
|
||||
if ($json.architecture.$architecture.$prop -and $json.autoupdate.architecture.$architecture.$prop) {
|
||||
$json.architecture.$architecture.$prop = substitute (arch_specific $prop $json.autoupdate $architecture) $substitutions
|
||||
process {
|
||||
foreach ($currentProperty in $Property) {
|
||||
if ($currentProperty -eq 'hash') {
|
||||
# Update hash
|
||||
if ($Manifest.hash) {
|
||||
# Global
|
||||
$newURL = substitute $Manifest.autoupdate.url $Substitutions
|
||||
$newHash = HashHelper -AppName $AppName -Version $Version -HashExtraction $Manifest.autoupdate.hash -URL $newURL -Substitutions $Substitutions
|
||||
$Manifest.hash, $hasPropertyChanged = PropertyHelper -Property $Manifest.hash -Value $newHash
|
||||
$hasManifestChanged = $hasManifestChanged -or $hasPropertyChanged
|
||||
} else {
|
||||
# Arch-spec
|
||||
$Manifest.architecture | Get-Member -MemberType NoteProperty | ForEach-Object {
|
||||
$arch = $_.Name
|
||||
$newURL = substitute (arch_specific 'url' $Manifest.autoupdate $arch) $Substitutions
|
||||
$newHash = HashHelper -AppName $AppName -Version $Version -HashExtraction (arch_specific 'hash' $Manifest.autoupdate $arch) -URL $newURL -Substitutions $Substitutions
|
||||
$Manifest.architecture.$arch.hash, $hasPropertyChanged = PropertyHelper -Property $Manifest.architecture.$arch.hash -Value $newHash
|
||||
$hasManifestChanged = $hasManifestChanged -or $hasPropertyChanged
|
||||
}
|
||||
}
|
||||
} elseif ($Manifest.$currentProperty -and $Manifest.autoupdate.$currentProperty) {
|
||||
# Update other property (global)
|
||||
$autoupdateProperty = $Manifest.autoupdate.$currentProperty
|
||||
$newValue = substitute $autoupdateProperty $Substitutions
|
||||
if (($autoupdateProperty.GetType().Name -eq 'Object[]') -and ($autoupdateProperty.Length -eq 1)) {
|
||||
# Make sure it's an array
|
||||
$newValue = ,$newValue
|
||||
}
|
||||
$Manifest.$currentProperty, $hasPropertyChanged = PropertyHelper -Property $Manifest.$currentProperty -Value $newValue
|
||||
$hasManifestChanged = $hasManifestChanged -or $hasPropertyChanged
|
||||
} elseif ($Manifest.architecture) {
|
||||
# Update other property (arch-spec)
|
||||
$Manifest.architecture | Get-Member -MemberType NoteProperty | ForEach-Object {
|
||||
$arch = $_.Name
|
||||
if ($Manifest.architecture.$arch.$currentProperty -and ($Manifest.autoupdate.architecture.$arch.$currentProperty -or $Manifest.autoupdate.$currentProperty)) {
|
||||
$autoupdateProperty = @(arch_specific $currentProperty $Manifest.autoupdate $arch)
|
||||
$newValue = substitute $autoupdateProperty $Substitutions
|
||||
if (($autoupdateProperty.GetType().Name -eq 'Object[]') -and ($autoupdateProperty.Length -eq 1)) {
|
||||
# Make sure it's an array
|
||||
$newValue = ,$newValue
|
||||
}
|
||||
$Manifest.architecture.$arch.$currentProperty, $hasPropertyChanged = PropertyHelper -Property $Manifest.architecture.$arch.$currentProperty -Value $newValue
|
||||
$hasManifestChanged = $hasManifestChanged -or $hasPropertyChanged
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
end {
|
||||
if ($Version -ne '' -and $Manifest.version -ne $Version) {
|
||||
$Manifest.version = $Version
|
||||
$hasManifestChanged = $true
|
||||
}
|
||||
return $hasManifestChanged
|
||||
}
|
||||
}
|
||||
|
||||
function get_version_substitutions([String] $version, [Hashtable] $customMatches) {
|
||||
$firstPart = $version.Split('-') | Select-Object -first 1
|
||||
$lastPart = $version.Split('-') | Select-Object -last 1
|
||||
function Get-VersionSubstitution {
|
||||
param (
|
||||
[String]
|
||||
$Version,
|
||||
[Hashtable]
|
||||
$CustomMatches
|
||||
)
|
||||
|
||||
$firstPart = $Version.Split('-') | Select-Object -First 1
|
||||
$lastPart = $Version.Split('-') | Select-Object -Last 1
|
||||
$versionVariables = @{
|
||||
'$version' = $version;
|
||||
'$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;
|
||||
'$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.Set_Item('$matchHead', $Matches['head'])
|
||||
$versionVariables.Set_Item('$matchTail', $Matches['tail'])
|
||||
}
|
||||
if($customMatches) {
|
||||
$customMatches.GetEnumerator() | ForEach-Object {
|
||||
if($CustomMatches) {
|
||||
$CustomMatches.GetEnumerator() | ForEach-Object {
|
||||
if($_.Name -ne "0") {
|
||||
$versionVariables.Set_Item('$match' + (Get-Culture).TextInfo.ToTitleCase($_.Name), $_.Value)
|
||||
}
|
||||
@@ -349,85 +413,168 @@ function get_version_substitutions([String] $version, [Hashtable] $customMatches
|
||||
return $versionVariables
|
||||
}
|
||||
|
||||
function autoupdate([String] $app, $dir, $json, [String] $version, [Hashtable] $matches) {
|
||||
Write-Host -f DarkCyan "Autoupdating $app"
|
||||
$has_changes = $false
|
||||
$has_errors = $false
|
||||
[Bool]$valid = $true
|
||||
$substitutions = get_version_substitutions $version $matches
|
||||
function Invoke-AutoUpdate {
|
||||
param (
|
||||
[String]
|
||||
$AppName,
|
||||
[String]
|
||||
$Path,
|
||||
[PSObject]
|
||||
$Manifest,
|
||||
[String]
|
||||
$Version,
|
||||
[Hashtable]
|
||||
$CustomMatches
|
||||
)
|
||||
|
||||
if ($json.url) {
|
||||
# create new url
|
||||
$url = substitute $json.autoupdate.url $substitutions
|
||||
$valid = $true
|
||||
|
||||
if($valid) {
|
||||
# create hash
|
||||
$hash = get_hash_for_app $app $json.autoupdate.hash $version $url $substitutions
|
||||
if ($null -eq $hash) {
|
||||
$valid = $false
|
||||
Write-Host -f DarkRed "Could not find hash!"
|
||||
}
|
||||
}
|
||||
|
||||
# write changes to the json object
|
||||
if ($valid) {
|
||||
$has_changes = $true
|
||||
update_manifest_with_new_version $json $version $url $hash
|
||||
} else {
|
||||
$has_errors = $true
|
||||
throw "Could not update $app"
|
||||
}
|
||||
} else {
|
||||
$json.architecture | Get-Member -MemberType NoteProperty | ForEach-Object {
|
||||
$valid = $true
|
||||
$architecture = $_.Name
|
||||
|
||||
# create new url
|
||||
$url = substitute (arch_specific "url" $json.autoupdate $architecture) $substitutions
|
||||
$valid = $true
|
||||
|
||||
if($valid) {
|
||||
# create hash
|
||||
$hash = get_hash_for_app $app (arch_specific "hash" $json.autoupdate $architecture) $version $url $substitutions
|
||||
if ($null -eq $hash) {
|
||||
$valid = $false
|
||||
Write-Host -f DarkRed "Could not find hash!"
|
||||
}
|
||||
}
|
||||
|
||||
# write changes to the json object
|
||||
if ($valid) {
|
||||
$has_changes = $true
|
||||
update_manifest_with_new_version $json $version $url $hash $architecture
|
||||
} else {
|
||||
$has_errors = $true
|
||||
throw "Could not update $app $architecture"
|
||||
}
|
||||
}
|
||||
}
|
||||
Write-Host "Autoupdating $AppName" -ForegroundColor DarkCyan
|
||||
$substitutions = Get-VersionSubstitution $Version $CustomMatches
|
||||
|
||||
# update properties
|
||||
update_manifest_prop "extract_dir" $json $substitutions
|
||||
$updatedProperties = @(@($Manifest.autoupdate.PSObject.Properties.Name) -ne 'architecture')
|
||||
if ($Manifest.autoupdate.architecture) {
|
||||
$updatedProperties += $Manifest.autoupdate.architecture.PSObject.Properties | ForEach-Object { $_.Value.PSObject.Properties.Name }
|
||||
}
|
||||
if ($updatedProperties -contains 'url') {
|
||||
$updatedProperties += 'hash'
|
||||
}
|
||||
$updatedProperties = $updatedProperties | Select-Object -Unique
|
||||
debug [$updatedProperties]
|
||||
$hasChanged = Update-ManifestProperty -Manifest $Manifest -Property $updatedProperties -AppName $AppName -Version $Version -Substitutions $substitutions
|
||||
|
||||
# update license
|
||||
update_manifest_prop "license" $json $substitutions
|
||||
|
||||
if ($has_changes -and !$has_errors) {
|
||||
if ($hasChanged) {
|
||||
# write file
|
||||
Write-Host -f DarkGreen "Writing updated $app manifest"
|
||||
|
||||
$path = join-path $dir "$app.json"
|
||||
|
||||
$file_content = $json | ConvertToPrettyJson
|
||||
[System.IO.File]::WriteAllLines($path, $file_content)
|
||||
|
||||
Write-Host "Writing updated $AppName manifest" -ForegroundColor DarkGreen
|
||||
# Accept unusual Unicode characters
|
||||
# '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))
|
||||
# notes
|
||||
if ($json.autoupdate.note) {
|
||||
Write-Host ""
|
||||
Write-Host -f DarkYellow $json.autoupdate.note
|
||||
$note = "`nUpdating note:"
|
||||
if ($Manifest.autoupdate.note) {
|
||||
$note += "`nno-arch: $($Manifest.autoupdate.note)"
|
||||
$hasNote = $true
|
||||
}
|
||||
if ($Manifest.autoupdate.architecture) {
|
||||
'64bit', '32bit' | ForEach-Object {
|
||||
if ($Manifest.autoupdate.architecture.$_.note) {
|
||||
$note += "`n$_-arch: $($Manifest.autoupdate.architecture.$_.note)"
|
||||
$hasNote = $true
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($hasNote) {
|
||||
Write-Host $note -ForegroundColor DarkYellow
|
||||
}
|
||||
} else {
|
||||
Write-Host -f DarkGray "No updates for $app"
|
||||
# This if-else branch may not be in use.
|
||||
Write-Host "No updates for $AppName" -ForegroundColor DarkGray
|
||||
}
|
||||
}
|
||||
|
||||
## Helper Functions
|
||||
|
||||
function PropertyHelper {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Helper of updating property
|
||||
.DESCRIPTION
|
||||
Update manifest property (String, Array or PSCustomObject).
|
||||
.PARAMETER Property
|
||||
Property to be updated
|
||||
.PARAMETER Value
|
||||
New property values
|
||||
Update line by line
|
||||
.OUTPUTS
|
||||
System.Object[]
|
||||
The first element is new property, the second element is change flag
|
||||
#>
|
||||
param (
|
||||
[Object]$Property,
|
||||
[Object]$Value
|
||||
)
|
||||
$hasChanged = $false
|
||||
if (@($Property).Length -lt @($Value).Length) {
|
||||
$Property = $Value
|
||||
$hasChanged = $true
|
||||
} else {
|
||||
switch ($Property.GetType().Name) {
|
||||
'String' {
|
||||
$Value = $Value -as [String]
|
||||
if ($null -ne $Value) {
|
||||
$Property = $Value
|
||||
$hasChanged = $true
|
||||
}
|
||||
}
|
||||
'Object[]' {
|
||||
$Value = @($Value)
|
||||
for ($i = 0; $i -lt $Value.Length; $i++) {
|
||||
$Property[$i], $hasItemChanged = PropertyHelper -Property $Property[$i] -Value $Value[$i]
|
||||
$hasChanged = $hasChanged -or $hasItemChanged
|
||||
}
|
||||
}
|
||||
'PSCustomObject' {
|
||||
if ($Value -is [PSObject]) {
|
||||
foreach ($name in $Property.PSObject.Properties.Name) {
|
||||
if ($Value.$name) {
|
||||
$Property.$name, $hasItemChanged = PropertyHelper -Property $Property.$name -Value $Value.$name
|
||||
$hasChanged = $hasChanged -or $hasItemChanged
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $Property, $hasChanged
|
||||
}
|
||||
|
||||
function HashHelper {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Helper of getting file hash(es)
|
||||
.DESCRIPTION
|
||||
Extract or calculate file hash(es).
|
||||
If hash extraction templates are less then URLs, the last template will be reused for the rest URLs.
|
||||
.PARAMETER AppName
|
||||
Software name
|
||||
.PARAMETER Version
|
||||
Given software version
|
||||
.PARAMETER HashExtraction
|
||||
Hash extraction template(s)
|
||||
.PARAMETER URL
|
||||
New download URL(s), used to calculate hash locally (fallback)
|
||||
.PARAMETER Substitutions
|
||||
Hashtable of internal substitutable variables
|
||||
.OUTPUTS
|
||||
System.String
|
||||
Hash value (single URL)
|
||||
System.String[]
|
||||
Hash values (multi URLs)
|
||||
#>
|
||||
param (
|
||||
[String]
|
||||
$AppName,
|
||||
[String]
|
||||
$Version,
|
||||
[PSObject[]]
|
||||
$HashExtraction,
|
||||
[String[]]
|
||||
$URL,
|
||||
[HashTable]
|
||||
$Substitutions
|
||||
)
|
||||
$hash = @()
|
||||
for ($i = 0; $i -lt $URL.Length; $i++) {
|
||||
if ($null -eq $HashExtraction) {
|
||||
$currentHashExtraction = $null
|
||||
} else {
|
||||
$currentHashExtraction = $HashExtraction[$i], $HashExtraction[-1] | Select-Object -First 1
|
||||
}
|
||||
$hash += get_hash_for_app $AppName $currentHashExtraction $Version $URL[$i] $Substitutions
|
||||
if ($null -eq $hash[$i]) {
|
||||
throw "Could not update $AppName, hash for $(url_remote_filename $URL[$i]) failed!"
|
||||
}
|
||||
}
|
||||
return $hash
|
||||
}
|
||||
|
||||
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 | Where-Object { $_.Name.EndsWith('.json') } | ForEach-Object { $_.Name -replace '.json$', '' }
|
||||
}
|
||||
|
||||
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,76 +72,112 @@ 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 = git -C $path config remote.origin.url
|
||||
$bucket.Updated = git -C $path log --format='%aD' -n 1 | Get-Date
|
||||
} 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."
|
||||
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_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 = git -C "$bucketsdir\$bucket" 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 = git_cmd 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_clone "$repo" "`"$dir`"" -q
|
||||
git_cmd 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) {
|
||||
Push-Location $bucket_path
|
||||
$remote = git_config --get remote.origin.url
|
||||
if (Test-Path $bucket_path) {
|
||||
$remote = git -C "$bucket_path" config --get remote.origin.url
|
||||
# Support ssh and http syntax
|
||||
# git@PROVIDER:USER/REPO.git
|
||||
# https://PROVIDER/USER/REPO.git
|
||||
$remote -match '(@|:\/\/)(?<provider>.+)[:/](?<user>.*)\/(?<repo>.*)(\.git)?$' | Out-Null
|
||||
$url = "https://$($Matches.Provider)/$($Matches.User)/$($Matches.Repo)"
|
||||
Pop-Location
|
||||
}
|
||||
|
||||
if(!$url) { return 'Please contact the bucket maintainer!' }
|
||||
if (!$url) { return 'Please contact the bucket maintainer!' }
|
||||
|
||||
# Print only github repositories
|
||||
if ($url -like '*github*') {
|
||||
@@ -145,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 }
|
||||
|
||||
676
lib/core.ps1
676
lib/core.ps1
@@ -9,9 +9,17 @@ 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')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +49,10 @@ function load_cfg($file) {
|
||||
}
|
||||
|
||||
try {
|
||||
return (Get-Content $file -Raw | ConvertFrom-Json -ErrorAction Stop)
|
||||
# ReadAllLines will detect the encoding of the file automatically
|
||||
# Ref: https://docs.microsoft.com/en-us/dotnet/api/system.io.file.readalllines?view=netframework-4.5
|
||||
$content = [System.IO.File]::ReadAllLines($file)
|
||||
return ($content | ConvertFrom-Json -ErrorAction Stop)
|
||||
} catch {
|
||||
Write-Host "ERROR loading $file`: $($_.exception.message)"
|
||||
}
|
||||
@@ -54,27 +65,34 @@ function get_config($name, $default) {
|
||||
return $scoopConfig.$name
|
||||
}
|
||||
|
||||
function set_config($name, $value) {
|
||||
if($null -eq $scoopConfig -or $scoopConfig.Count -eq 0) {
|
||||
function set_config {
|
||||
Param (
|
||||
[ValidateNotNullOrEmpty()]
|
||||
$name,
|
||||
$value
|
||||
)
|
||||
|
||||
if ($null -eq $scoopConfig -or $scoopConfig.Count -eq 0) {
|
||||
ensure (Split-Path -Path $configFile) | Out-Null
|
||||
$scoopConfig = New-Object PSObject
|
||||
$scoopConfig | Add-Member -MemberType NoteProperty -Name $name -Value $value
|
||||
} else {
|
||||
if($value -eq [bool]::TrueString -or $value -eq [bool]::FalseString) {
|
||||
$value = [System.Convert]::ToBoolean($value)
|
||||
}
|
||||
if($null -eq $scoopConfig.$name) {
|
||||
$scoopConfig | Add-Member -MemberType NoteProperty -Name $name -Value $value
|
||||
} else {
|
||||
$scoopConfig.$name = $value
|
||||
}
|
||||
$scoopConfig = New-Object -TypeName PSObject
|
||||
}
|
||||
|
||||
if($null -eq $value) {
|
||||
if ($value -eq [bool]::TrueString -or $value -eq [bool]::FalseString) {
|
||||
$value = [System.Convert]::ToBoolean($value)
|
||||
}
|
||||
|
||||
if ($null -eq $scoopConfig.$name) {
|
||||
$scoopConfig | Add-Member -MemberType NoteProperty -Name $name -Value $value
|
||||
} else {
|
||||
$scoopConfig.$name = $value
|
||||
}
|
||||
|
||||
if ($null -eq $value) {
|
||||
$scoopConfig.PSObject.Properties.Remove($name)
|
||||
}
|
||||
|
||||
ConvertTo-Json $scoopConfig | Set-Content $configFile -Encoding ASCII
|
||||
# Save config with UTF8NoBOM encoding
|
||||
ConvertTo-Json $scoopConfig | Out-UTF8File -FilePath $configFile
|
||||
return $scoopConfig
|
||||
}
|
||||
|
||||
@@ -107,6 +125,15 @@ 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"
|
||||
}
|
||||
cmd.exe /d /c $cmd
|
||||
}
|
||||
|
||||
# helper functions
|
||||
function coalesce($a, $b) { if($a) { return $a } $b }
|
||||
|
||||
@@ -168,6 +195,9 @@ function filesize($length) {
|
||||
} elseif($length -gt $kb) {
|
||||
"{0:n1} KB" -f ($length / $kb)
|
||||
} else {
|
||||
if ($null -eq $length) {
|
||||
$length = 0
|
||||
}
|
||||
"$($length) B"
|
||||
}
|
||||
}
|
||||
@@ -178,6 +208,16 @@ function appsdir($global) { "$(basedir $global)\apps" }
|
||||
function shimdir($global) { "$(basedir $global)\shims" }
|
||||
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) {
|
||||
$version = Select-CurrentVersion -App $app -Global:$global
|
||||
} else {
|
||||
$version = 'current'
|
||||
}
|
||||
"$(appdir $app $global)\$version"
|
||||
}
|
||||
|
||||
function persistdir($app, $global) { "$(basedir $global)\persist\$app" }
|
||||
function usermanifestsdir { "$(basedir)\workspace" }
|
||||
function usermanifest($app) { "$(usermanifestsdir)\$app.json" }
|
||||
@@ -185,21 +225,31 @@ function cache_path($app, $version, $url) { "$cachedir\$app#$version#$($url -rep
|
||||
|
||||
# apps
|
||||
function sanitary_path($path) { return [regex]::replace($path, "[/\\?:*<>|]", "") }
|
||||
function installed($app, $global=$null) {
|
||||
if($null -eq $global) { return (installed $app $true) -or (installed $app $false) }
|
||||
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
|
||||
$app = $app.split("/")[-1]
|
||||
return is_directory (appdir $app $global)
|
||||
$app = ($app -split '/|\\')[-1]
|
||||
return $null -ne (Select-CurrentVersion -AppName $app -Global:$global)
|
||||
}
|
||||
function installed_apps($global) {
|
||||
$dir = appsdir $global
|
||||
if(test-path $dir) {
|
||||
if (Test-Path $dir) {
|
||||
Get-ChildItem $dir | Where-Object { $_.psiscontainer -and $_.name -ne 'scoop' } | ForEach-Object { $_.name }
|
||||
}
|
||||
}
|
||||
|
||||
# check whether the app failed to install
|
||||
function failed($app, $global) {
|
||||
$app = ($app -split '/|\\')[-1]
|
||||
$appPath = appdir $app $global
|
||||
$hasCurrent = (get_config NO_JUNCTIONS) -or (Test-Path "$appPath\current")
|
||||
return (Test-Path $appPath) -and !($hasCurrent -and (installed $app $global))
|
||||
}
|
||||
|
||||
function file_path($app, $file) {
|
||||
Show-DeprecatedWarning $MyInvocation 'Get-AppFilePath'
|
||||
Get-AppFilePath -App $app -File $file
|
||||
@@ -207,6 +257,7 @@ function file_path($app, $file) {
|
||||
|
||||
function Get-AppFilePath {
|
||||
[CmdletBinding()]
|
||||
[OutputType([String])]
|
||||
param(
|
||||
[Parameter(Mandatory = $true, Position = 0)]
|
||||
[String]
|
||||
@@ -217,14 +268,14 @@ function Get-AppFilePath {
|
||||
)
|
||||
|
||||
# normal path to file
|
||||
$Path = "$(versiondir $App 'current' $false)\$File"
|
||||
if(Test-Path $Path) {
|
||||
$Path = "$(currentdir $App $false)\$File"
|
||||
if (Test-Path $Path) {
|
||||
return $Path
|
||||
}
|
||||
|
||||
# global path to file
|
||||
$Path = "$(versiondir $App 'current' $true)\$File"
|
||||
if(Test-Path $Path) {
|
||||
$Path = "$(currentdir $App $true)\$File"
|
||||
if (Test-Path $Path) {
|
||||
return $Path
|
||||
}
|
||||
|
||||
@@ -241,40 +292,78 @@ Function Test-CommandAvailable {
|
||||
|
||||
function Get-HelperPath {
|
||||
[CmdletBinding()]
|
||||
[OutputType([String])]
|
||||
param(
|
||||
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
|
||||
[ValidateSet('7zip', 'Lessmsi', 'Innounp', 'Dark', 'Aria2')]
|
||||
[ValidateSet('7zip', 'Lessmsi', 'Innounp', 'Dark', 'Aria2', 'Zstd')]
|
||||
[String]
|
||||
$Helper
|
||||
)
|
||||
begin {
|
||||
$HelperPath = $null
|
||||
}
|
||||
process {
|
||||
switch ($Helper) {
|
||||
'7zip' {
|
||||
$HelperPath = Get-AppFilePath '7zip' '7z.exe'
|
||||
if ([String]::IsNullOrEmpty($HelperPath)) {
|
||||
$HelperPath = Get-AppFilePath '7zip-zstd' '7z.exe'
|
||||
}
|
||||
}
|
||||
'Lessmsi' { $HelperPath = Get-AppFilePath 'lessmsi' 'lessmsi.exe' }
|
||||
'Innounp' { $HelperPath = Get-AppFilePath 'innounp' 'innounp.exe' }
|
||||
'Dark' {
|
||||
$HelperPath = Get-AppFilePath 'dark' 'dark.exe'
|
||||
if ([String]::IsNullOrEmpty($HelperPath)) {
|
||||
$HelperPath = Get-AppFilePath 'wixtoolset' 'dark.exe'
|
||||
}
|
||||
}
|
||||
'Aria2' { $HelperPath = Get-AppFilePath 'aria2' 'aria2c.exe' }
|
||||
'Zstd' { $HelperPath = Get-AppFilePath 'zstd' 'zstd.exe' }
|
||||
}
|
||||
|
||||
$HelperPath = $null
|
||||
switch ($Helper) {
|
||||
'7zip' {
|
||||
$HelperPath = Get-AppFilePath '7zip' '7z.exe'
|
||||
if([String]::IsNullOrEmpty($HelperPath)) {
|
||||
$HelperPath = Get-AppFilePath '7zip-zstd' '7z.exe'
|
||||
}
|
||||
}
|
||||
'Lessmsi' { $HelperPath = Get-AppFilePath 'lessmsi' 'lessmsi.exe' }
|
||||
'Innounp' { $HelperPath = Get-AppFilePath 'innounp' 'innounp.exe' }
|
||||
'Dark' {
|
||||
$HelperPath = Get-AppFilePath 'dark' 'dark.exe'
|
||||
if([String]::IsNullOrEmpty($HelperPath)) {
|
||||
$HelperPath = Get-AppFilePath 'wixtoolset' 'dark.exe'
|
||||
}
|
||||
}
|
||||
'Aria2' { $HelperPath = Get-AppFilePath 'aria2' 'aria2c.exe' }
|
||||
return $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
|
||||
}
|
||||
|
||||
return $HelperPath
|
||||
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(
|
||||
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
|
||||
[ValidateSet('7zip', 'Lessmsi', 'Innounp', 'Dark', 'Aria2')]
|
||||
[ValidateSet('7zip', 'Lessmsi', 'Innounp', 'Dark', 'Aria2', 'Zstd')]
|
||||
[String]
|
||||
$Helper
|
||||
)
|
||||
@@ -288,33 +377,41 @@ function Test-Aria2Enabled {
|
||||
|
||||
function app_status($app, $global) {
|
||||
$status = @{}
|
||||
$status.installed = (installed $app $global)
|
||||
$status.version = current_version $app $global
|
||||
$status.installed = installed $app $global
|
||||
$status.version = Select-CurrentVersion -AppName $app -Global:$global
|
||||
$status.latest_version = $status.version
|
||||
|
||||
$install_info = install_info $app $status.version $global
|
||||
|
||||
$status.failed = (!$install_info -or !$status.version)
|
||||
$status.failed = failed $app $global
|
||||
$status.hold = ($install_info.hold -eq $true)
|
||||
|
||||
$manifest = manifest $app $install_info.bucket $install_info.url
|
||||
$status.removed = (!$manifest)
|
||||
if($manifest.version) {
|
||||
if ($manifest.version) {
|
||||
$status.latest_version = $manifest.version
|
||||
}
|
||||
|
||||
$status.outdated = $false
|
||||
if($status.version -and $status.latest_version) {
|
||||
$status.outdated = ((compare_versions $status.latest_version $status.version) -gt 0)
|
||||
if ($status.version -and $status.latest_version) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
$status.missing_deps = @()
|
||||
$deps = @(runtime_deps $manifest) | Where-Object {
|
||||
$app, $bucket, $null = parse_app $_
|
||||
return !(installed $app)
|
||||
$deps = @($manifest.depends) | Where-Object {
|
||||
if ($null -eq $_) {
|
||||
return $null
|
||||
} else {
|
||||
$app, $bucket, $null = parse_app $_
|
||||
return !(installed $app)
|
||||
}
|
||||
}
|
||||
if($deps) {
|
||||
$status.missing_deps += ,$deps
|
||||
if ($deps) {
|
||||
$status.missing_deps += , $deps
|
||||
}
|
||||
|
||||
return $status
|
||||
@@ -351,11 +448,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 }
|
||||
@@ -405,12 +507,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
|
||||
}
|
||||
@@ -418,9 +520,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
|
||||
@@ -428,10 +553,17 @@ function Invoke-ExternalCommand {
|
||||
error $_.Exception.Message
|
||||
return $false
|
||||
}
|
||||
if ($LogPath -and ($FilePath -notmatch '(^|\W)msiexec($|\W)')) {
|
||||
Out-File -FilePath $LogPath -Encoding ASCII -Append -InputObject $Process.StandardOutput.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) {
|
||||
@@ -501,12 +633,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))"
|
||||
}
|
||||
|
||||
@@ -519,95 +651,196 @@ function movedir($from, $to) {
|
||||
}
|
||||
|
||||
function get_app_name($path) {
|
||||
if ($path -match '([^/\\]+)[/\\]current[/\\]') {
|
||||
return $matches[1].tolower()
|
||||
if ((Test-Path (appsdir $false)) -and ($path -match "$([Regex]::Escape($(Convert-Path (appsdir $false))))[/\\]([^/\\]+)")) {
|
||||
$appName = $Matches[1].ToLower()
|
||||
} elseif ((Test-Path (appsdir $true)) -and ($path -match "$([Regex]::Escape($(Convert-Path (appsdir $true))))[/\\]([^/\\]+)")) {
|
||||
$appName = $Matches[1].ToLower()
|
||||
} else {
|
||||
$appName = ''
|
||||
}
|
||||
return ''
|
||||
return $appName
|
||||
}
|
||||
|
||||
function get_app_name_from_ps1_shim($shim_ps1) {
|
||||
if (!(Test-Path($shim_ps1))) {
|
||||
function get_app_name_from_shim($shim) {
|
||||
if (!(Test-Path($shim))) {
|
||||
return ''
|
||||
}
|
||||
$content = (Get-Content $shim_ps1 -Encoding utf8) -join ' '
|
||||
$content = (Get-Content $shim -Encoding UTF8) -join ' '
|
||||
return get_app_name $content
|
||||
}
|
||||
|
||||
function warn_on_overwrite($shim_ps1, $path) {
|
||||
if (!(Test-Path($shim_ps1))) {
|
||||
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
|
||||
}
|
||||
$shim_app = get_app_name_from_ps1_shim $shim_ps1
|
||||
$shim_app = get_app_name_from_shim $shim
|
||||
$path_app = get_app_name $path
|
||||
if ($shim_app -eq $path_app) {
|
||||
return
|
||||
} else {
|
||||
if (Test-Path -Path "$shim.$path_app" -PathType Leaf) {
|
||||
Remove-Item -Path "$shim.$path_app" -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
Rename-Item -Path $shim -NewName "$shim.$shim_app" -ErrorAction SilentlyContinue
|
||||
}
|
||||
$filename = [System.IO.Path]::GetFileName($path)
|
||||
warn "Overwriting shim to $filename installed from $shim_app"
|
||||
$shimname = (fname $shim) -replace '\.shim$', '.exe'
|
||||
$filename = (fname $path) -replace '\.shim$', '.exe'
|
||||
warn "Overwriting shim ('$shimname' -> '$filename')$(if ($shim_app) { ' installed from ' + $shim_app })"
|
||||
}
|
||||
|
||||
function shim($path, $global, $name, $arg) {
|
||||
if(!(test-path $path)) { abort "Can't shim '$(fname $path)': couldn't find '$path'." }
|
||||
if (!(Test-Path $path)) { abort "Can't shim '$(fname $path)': couldn't find '$path'." }
|
||||
$abs_shimdir = ensure (shimdir $global)
|
||||
if(!$name) { $name = strip_ext (fname $path) }
|
||||
ensure_in_path $abs_shimdir $global
|
||||
if (!$name) { $name = strip_ext (fname $path) }
|
||||
|
||||
$shim = "$abs_shimdir\$($name.tolower())"
|
||||
|
||||
warn_on_overwrite "$shim.ps1" $path
|
||||
|
||||
# convert to relative path
|
||||
Push-Location $abs_shimdir
|
||||
$relative_path = resolve-path -relative $path
|
||||
$relative_path = Resolve-Path -Relative $path
|
||||
Pop-Location
|
||||
$resolved_path = resolve-path $path
|
||||
$resolved_path = Resolve-Path $path
|
||||
|
||||
# if $path points to another drive resolve-path prepends .\ which could break shims
|
||||
if($relative_path -match "^(.\\[\w]:).*$") {
|
||||
write-output "`$path = `"$path`"" | out-file "$shim.ps1" -encoding utf8
|
||||
} else {
|
||||
# Setting PSScriptRoot in Shim if it is not defined, so the shim doesn't break in PowerShell 2.0
|
||||
Write-Output "if (!(Test-Path Variable:PSScriptRoot)) { `$PSScriptRoot = Split-Path `$MyInvocation.MyCommand.Path -Parent }" | Out-File "$shim.ps1" -Encoding utf8
|
||||
write-output "`$path = join-path `"`$psscriptroot`" `"$relative_path`"" | out-file "$shim.ps1" -Encoding utf8 -Append
|
||||
}
|
||||
|
||||
if($path -match '\.jar$') {
|
||||
"if(`$myinvocation.expectingInput) { `$input | & java -jar `$path $arg @args } else { & java -jar `$path $arg @args }" | out-file "$shim.ps1" -encoding utf8 -append
|
||||
} else {
|
||||
"if(`$myinvocation.expectingInput) { `$input | & `$path $arg @args } else { & `$path $arg @args }" | out-file "$shim.ps1" -encoding utf8 -append
|
||||
}
|
||||
|
||||
if($path -match '\.(exe|com)$') {
|
||||
if ($path -match '\.(exe|com)$') {
|
||||
# for programs with no awareness of any shell
|
||||
Copy-Item "$(versiondir 'scoop' 'current')\supporting\shimexe\bin\shim.exe" "$shim.exe" -force
|
||||
write-output "path = $resolved_path" | out-file "$shim.shim" -encoding utf8
|
||||
if($arg) {
|
||||
write-output "args = $arg" | out-file "$shim.shim" -encoding utf8 -append
|
||||
warn_on_overwrite "$shim.shim" $path
|
||||
Copy-Item (get_shim_path) "$shim.exe" -Force
|
||||
Write-Output "path = `"$resolved_path`"" | Out-UTF8File "$shim.shim"
|
||||
if ($arg) {
|
||||
Write-Output "args = $arg" | Out-UTF8File "$shim.shim" -Append
|
||||
}
|
||||
} elseif($path -match '\.(bat|cmd)$') {
|
||||
} elseif ($path -match '\.(bat|cmd)$') {
|
||||
# shim .bat, .cmd so they can be used by programs with no awareness of PSH
|
||||
"@`"$resolved_path`" $arg %*" | out-file "$shim.cmd" -encoding ascii
|
||||
warn_on_overwrite "$shim.cmd" $path
|
||||
@(
|
||||
"@rem $resolved_path",
|
||||
"@`"$resolved_path`" $arg %*"
|
||||
) -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-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
|
||||
$ps1text = if ($relative_path -match '^(\.\\)?\w:.*$') {
|
||||
@(
|
||||
"# $resolved_path",
|
||||
"`$path = `"$path`"",
|
||||
"if (`$MyInvocation.ExpectingInput) { `$input | & `$path $arg @args } else { & `$path $arg @args }",
|
||||
"exit `$LASTEXITCODE"
|
||||
)
|
||||
} else {
|
||||
@(
|
||||
"# $resolved_path",
|
||||
"`$path = Join-Path `$PSScriptRoot `"$relative_path`"",
|
||||
"if (`$MyInvocation.ExpectingInput) { `$input | & `$path $arg @args } else { & `$path $arg @args }",
|
||||
"exit `$LASTEXITCODE"
|
||||
)
|
||||
}
|
||||
$ps1text -join "`r`n" | Out-UTF8File "$shim.ps1"
|
||||
|
||||
"#!/bin/sh`nMSYS2_ARG_CONV_EXCL=/C cmd.exe /C `"$resolved_path`" $arg `"$@`"" | out-file $shim -encoding ascii
|
||||
} elseif($path -match '\.ps1$') {
|
||||
# make ps1 accessible from cmd.exe
|
||||
"@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= )
|
||||
powershell -noprofile -ex unrestricted `"& '$resolved_path' $arg %args%;exit `$lastexitcode`"" | out-file "$shim.cmd" -encoding ascii
|
||||
warn_on_overwrite "$shim.cmd" $path
|
||||
@(
|
||||
"@rem $resolved_path",
|
||||
"@echo off",
|
||||
"where /q pwsh.exe",
|
||||
"if %errorlevel% equ 0 (",
|
||||
" pwsh -noprofile -ex unrestricted -file `"$resolved_path`" $arg %*",
|
||||
") else (",
|
||||
" powershell -noprofile -ex unrestricted -file `"$resolved_path`" $arg %*",
|
||||
")"
|
||||
) -join "`r`n" | Out-UTF8File "$shim.cmd"
|
||||
|
||||
"#!/bin/sh`npowershell.exe -noprofile -ex unrestricted `"$resolved_path`" $arg `"$@`"" | out-file $shim -encoding ascii
|
||||
} elseif($path -match '\.jar$') {
|
||||
"@java -jar `"$resolved_path`" $arg %*" | out-file "$shim.cmd" -encoding ascii
|
||||
"#!/bin/sh`njava -jar `"$resolved_path`" $arg `"$@`"" | out-file $shim -encoding ascii
|
||||
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 `"$@`"",
|
||||
"else",
|
||||
" powershell.exe -noprofile -ex unrestricted -file `"$resolved_path`" $arg `"$@`"",
|
||||
"fi"
|
||||
) -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-UTF8File "$shim.cmd"
|
||||
|
||||
warn_on_overwrite $shim $path
|
||||
@(
|
||||
"#!/bin/sh",
|
||||
"# $resolved_path",
|
||||
"java.exe -jar `"$resolved_path`" $arg `"$@`""
|
||||
) -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-UTF8File "$shim.cmd"
|
||||
|
||||
warn_on_overwrite $shim $path
|
||||
@(
|
||||
"#!/bin/sh",
|
||||
"# $resolved_path",
|
||||
"python.exe `"$resolved_path`" $arg `"$@`""
|
||||
) -join "`n" | Out-UTF8File $shim -NoNewLine
|
||||
} else {
|
||||
warn_on_overwrite "$shim.cmd" $path
|
||||
# find path to Git's bash so that batch scripts can run bash scripts
|
||||
$gitdir = (Get-Item (Get-CommandPath git) -ErrorAction:Stop).Directory.Parent
|
||||
if ($gitdir.FullName -imatch 'mingw') {
|
||||
$gitdir = $gitdir.Parent
|
||||
}
|
||||
@(
|
||||
"@rem $resolved_path",
|
||||
"@`"$(Join-Path (Join-Path $gitdir.FullName 'bin') 'bash.exe')`" `"$resolved_path`" $arg %*"
|
||||
) -join "`r`n" | Out-UTF8File "$shim.cmd"
|
||||
|
||||
warn_on_overwrite $shim $path
|
||||
@(
|
||||
"#!/bin/sh",
|
||||
"# $resolved_path",
|
||||
"`"$resolved_path`" $arg `"$@`""
|
||||
) -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'
|
||||
switch ($shim_version) {
|
||||
'71' { $shim_path = "$(versiondir 'scoop' 'current')\supporting\shims\71\shim.exe"; Break }
|
||||
'scoopcs' { $shim_path = "$(versiondir 'scoop' 'current')\supporting\shimexe\bin\shim.exe"; Break }
|
||||
'kiennq' { Break } # for backward compatibility
|
||||
'default' { Break }
|
||||
default { warn "Unknown shim version: '$shim_version'" }
|
||||
}
|
||||
return $shim_path
|
||||
}
|
||||
|
||||
function search_in_path($target) {
|
||||
$path = (env 'PATH' $false) + ";" + (env 'PATH' $true)
|
||||
foreach($dir in $path.split(';')) {
|
||||
@@ -642,6 +875,7 @@ function ensure_architecture($architecture_opt) {
|
||||
|
||||
function Confirm-InstallationStatus {
|
||||
[CmdletBinding()]
|
||||
[OutputType([Object[]])]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[String[]]
|
||||
@@ -650,29 +884,32 @@ 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 (installed $App $true) {
|
||||
$Installed += ,@($App, $true)
|
||||
} elseif (installed $App $false) {
|
||||
error "'$App' isn't installed globally, but it is installed for your account."
|
||||
if (Test-Path (appdir $App $true)) {
|
||||
$Installed += , @($App, $true)
|
||||
} elseif (Test-Path (appdir $App $false)) {
|
||||
error "'$App' isn't installed globally, but it may be installed locally."
|
||||
warn "Try again without the --global (or -g) flag instead."
|
||||
} else {
|
||||
error "'$App' isn't installed."
|
||||
}
|
||||
} else {
|
||||
if(installed $App $false) {
|
||||
$Installed += ,@($App, $false)
|
||||
} elseif (installed $App $true) {
|
||||
error "'$App' isn't installed for your account, but it is installed globally."
|
||||
if (Test-Path (appdir $App $false)) {
|
||||
$Installed += , @($App, $false)
|
||||
} elseif (Test-Path (appdir $App $true)) {
|
||||
error "'$App' isn't installed locally, but it may be installed globally."
|
||||
warn "Try again with the --global (or -g) flag instead."
|
||||
} else {
|
||||
error "'$App' isn't installed."
|
||||
}
|
||||
}
|
||||
if (failed $App $Global) {
|
||||
error "'$App' isn't installed correctly."
|
||||
}
|
||||
}
|
||||
return ,$Installed
|
||||
return , $Installed
|
||||
}
|
||||
|
||||
function strip_path($orig_path, $dir) {
|
||||
@@ -708,12 +945,6 @@ function remove_from_path($dir, $global) {
|
||||
if($was_in_path) { $env:PATH = $newpath }
|
||||
}
|
||||
|
||||
function ensure_scoop_in_path($global) {
|
||||
$abs_shimdir = ensure (shimdir $global)
|
||||
# be aggressive (b-e-aggressive) and install scoop first in the path
|
||||
ensure_in_path $abs_shimdir $global
|
||||
}
|
||||
|
||||
function ensure_robocopy_in_path {
|
||||
if(!(Test-CommandAvailable robocopy)) {
|
||||
shim "C:\Windows\System32\Robocopy.exe" $false
|
||||
@@ -741,66 +972,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) {
|
||||
@@ -815,7 +998,7 @@ function show_app($app, $bucket, $version) {
|
||||
|
||||
function last_scoop_update() {
|
||||
# PowerShell 6 returns an DateTime Object
|
||||
$last_update = (scoop config lastupdate)
|
||||
$last_update = (get_config lastupdate)
|
||||
|
||||
if ($null -ne $last_update -and $last_update.GetType() -eq [System.String]) {
|
||||
try {
|
||||
@@ -831,7 +1014,7 @@ function is_scoop_outdated() {
|
||||
$last_update = $(last_scoop_update)
|
||||
$now = [System.DateTime]::Now
|
||||
if($null -eq $last_update) {
|
||||
scoop config lastupdate $now.ToString('o')
|
||||
set_config lastupdate $now.ToString('o')
|
||||
# enforce an update for the first time
|
||||
return $true
|
||||
}
|
||||
@@ -839,18 +1022,27 @@ function is_scoop_outdated() {
|
||||
}
|
||||
|
||||
function substitute($entity, [Hashtable] $params, [Bool]$regexEscape = $false) {
|
||||
if ($entity -is [Array]) {
|
||||
return $entity | ForEach-Object { substitute $_ $params $regexEscape}
|
||||
} elseif ($entity -is [String]) {
|
||||
$params.GetEnumerator() | ForEach-Object {
|
||||
if($regexEscape -eq $false -or $null -eq $_.Value) {
|
||||
$entity = $entity.Replace($_.Name, $_.Value)
|
||||
} else {
|
||||
$entity = $entity.Replace($_.Name, [Regex]::Escape($_.Value))
|
||||
$newentity = $entity
|
||||
if ($null -ne $newentity) {
|
||||
switch ($entity.GetType().Name) {
|
||||
'String' {
|
||||
$params.GetEnumerator() | ForEach-Object {
|
||||
if ($regexEscape -eq $false -or $null -eq $_.Value) {
|
||||
$newentity = $newentity.Replace($_.Name, $_.Value)
|
||||
} else {
|
||||
$newentity = $newentity.Replace($_.Name, [Regex]::Escape($_.Value))
|
||||
}
|
||||
}
|
||||
}
|
||||
'Object[]' {
|
||||
$newentity = $entity | ForEach-Object { ,(substitute $_ $params $regexEscape) }
|
||||
}
|
||||
'PSCustomObject' {
|
||||
$newentity.PSObject.Properties | ForEach-Object { $_.Value = substitute $_.Value $params $regexEscape }
|
||||
}
|
||||
}
|
||||
return $entity
|
||||
}
|
||||
return $newentity
|
||||
}
|
||||
|
||||
function format_hash([String] $hash) {
|
||||
@@ -893,6 +1085,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
|
||||
@@ -900,6 +1096,7 @@ function handle_special_urls($url)
|
||||
$Body = @{
|
||||
projectUri = $Matches.name;
|
||||
fileName = $Matches.filename;
|
||||
source = 'CF';
|
||||
isLatestVersion = $true
|
||||
}
|
||||
if ((Invoke-RestMethod -Uri $url) -match '"p":"(?<pid>[a-f0-9]{24}).*?"r":"(?<rid>[a-f0-9]{24})') {
|
||||
@@ -917,6 +1114,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
|
||||
}
|
||||
|
||||
@@ -942,6 +1151,33 @@ function get_magic_bytes_pretty($file, $glue = ' ') {
|
||||
return (get_magic_bytes $file | ForEach-Object { $_.ToString('x2') }) -join $glue
|
||||
}
|
||||
|
||||
function Out-UTF8File {
|
||||
param(
|
||||
[Parameter(Mandatory = $True, Position = 0)]
|
||||
[Alias("Path")]
|
||||
[String] $FilePath,
|
||||
[Switch] $Append,
|
||||
[Switch] $NoNewLine,
|
||||
[Parameter(ValueFromPipeline = $True)]
|
||||
[PSObject] $InputObject
|
||||
)
|
||||
process {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
##################
|
||||
# Core Bootstrap #
|
||||
##################
|
||||
@@ -950,19 +1186,6 @@ function get_magic_bytes_pretty($file, $glue = ' ') {
|
||||
# for all communication with api.github.com
|
||||
Optimize-SecurityProtocol
|
||||
|
||||
# Scoop root directory
|
||||
$scoopdir = $env:SCOOP, (get_config 'rootPath'), "$env: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
|
||||
|
||||
# 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
|
||||
|
||||
# Scoop config file migration
|
||||
$configHome = $env:XDG_CONFIG_HOME, "$env:USERPROFILE\.config" | Select-Object -First 1
|
||||
$configFile = "$configHome\scoop\config.json"
|
||||
@@ -976,5 +1199,18 @@ if ((Test-Path "$env:USERPROFILE\.scoop") -and !(Test-Path $configFile)) {
|
||||
# Load Scoop config
|
||||
$scoopConfig = load_cfg $configFile
|
||||
|
||||
# Scoop root directory
|
||||
$scoopdir = $env:SCOOP, (get_config 'rootPath'), "$env: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
|
||||
|
||||
# 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
|
||||
|
||||
# Setup proxy globally
|
||||
setup_proxy
|
||||
|
||||
@@ -1,40 +1,3 @@
|
||||
function Test-7zipRequirement {
|
||||
[CmdletBinding(DefaultParameterSetName = "URL")]
|
||||
[OutputType([Boolean])]
|
||||
param (
|
||||
[Parameter(Mandatory = $true, ParameterSetName = "URL")]
|
||||
[String[]]
|
||||
$URL,
|
||||
[Parameter(Mandatory = $true, ParameterSetName = "File")]
|
||||
[String]
|
||||
$File
|
||||
)
|
||||
if ($URL) {
|
||||
if ((get_config 7ZIPEXTRACT_USE_EXTERNAL)) {
|
||||
return $false
|
||||
} else {
|
||||
return ($URL | Where-Object { Test-7zipRequirement -File $_ }).Count -gt 0
|
||||
}
|
||||
} else {
|
||||
return $File -match '\.((gz)|(tar)|(tgz)|(lzma)|(bz)|(bz2)|(7z)|(rar)|(iso)|(xz)|(lzh)|(nupkg))$'
|
||||
}
|
||||
}
|
||||
|
||||
function Test-LessmsiRequirement {
|
||||
[CmdletBinding()]
|
||||
[OutputType([Boolean])]
|
||||
param (
|
||||
[Parameter(Mandatory = $true)]
|
||||
[String[]]
|
||||
$URL
|
||||
)
|
||||
if ((get_config MSIEXTRACT_USE_LESSMSI)) {
|
||||
return ($URL | Where-Object { $_ -match '\.msi$' }).Count -gt 0
|
||||
} else {
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
function Expand-7zipArchive {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
@@ -49,7 +12,7 @@ function Expand-7zipArchive {
|
||||
[Parameter(ValueFromRemainingArguments = $true)]
|
||||
[String]
|
||||
$Switches,
|
||||
[ValidateSet("All", "Skip", "Rename")]
|
||||
[ValidateSet('All', 'Skip', 'Rename')]
|
||||
[String]
|
||||
$Overwrite,
|
||||
[Switch]
|
||||
@@ -57,26 +20,26 @@ function Expand-7zipArchive {
|
||||
)
|
||||
if ((get_config 7ZIPEXTRACT_USE_EXTERNAL)) {
|
||||
try {
|
||||
$7zPath = (Get-Command '7z' -CommandType Application | Select-Object -First 1).Source
|
||||
$7zPath = (Get-Command '7z' -CommandType Application -ErrorAction Stop | Select-Object -First 1).Source
|
||||
} catch [System.Management.Automation.CommandNotFoundException] {
|
||||
abort "Cannot 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 '7ZIPEXTRACT_USE_EXTERNAL' is 'true'!`nRun 'scoop config 7ZIPEXTRACT_USE_EXTERNAL 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')
|
||||
$ArgList = @('x', $Path, "-o$DestinationPath", '-y')
|
||||
$IsTar = ((strip_ext $Path) -match '\.tar$') -or ($Path -match '\.t[abgpx]z2?$')
|
||||
if (!$IsTar -and $ExtractDir) {
|
||||
$ArgList += "-ir!`"$ExtractDir\*`""
|
||||
$ArgList += "-ir!$ExtractDir\*"
|
||||
}
|
||||
if ($Switches) {
|
||||
$ArgList += (-split $Switches)
|
||||
}
|
||||
switch ($Overwrite) {
|
||||
"All" { $ArgList += "-aoa" }
|
||||
"Skip" { $ArgList += "-aos" }
|
||||
"Rename" { $ArgList += "-aou" }
|
||||
'All' { $ArgList += '-aoa' }
|
||||
'Skip' { $ArgList += '-aos' }
|
||||
'Rename' { $ArgList += '-aou' }
|
||||
}
|
||||
$Status = Invoke-ExternalCommand $7zPath $ArgList -LogPath $LogPath
|
||||
if (!$Status) {
|
||||
@@ -90,9 +53,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)[-4] -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."
|
||||
@@ -100,7 +64,63 @@ 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Expand-ZstdArchive {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
|
||||
[String]
|
||||
$Path,
|
||||
[Parameter(Position = 1)]
|
||||
[String]
|
||||
$DestinationPath = (Split-Path $Path),
|
||||
[String]
|
||||
$ExtractDir,
|
||||
[Parameter(ValueFromRemainingArguments = $true)]
|
||||
[String]
|
||||
$Switches,
|
||||
[Switch]
|
||||
$Removal
|
||||
)
|
||||
$ZstdPath = Get-HelperPath -Helper Zstd
|
||||
$LogPath = Join-Path (Split-Path $Path) 'zstd.log'
|
||||
$DestinationPath = $DestinationPath.TrimEnd('\')
|
||||
ensure $DestinationPath | Out-Null
|
||||
$ArgList = @('-d', $Path, '--output-dir-flat', $DestinationPath, '-f', '-v')
|
||||
|
||||
if ($Switches) {
|
||||
$ArgList += (-split $Switches)
|
||||
}
|
||||
if ($Removal) {
|
||||
# Remove original archive file
|
||||
$ArgList += '--rm'
|
||||
}
|
||||
$Status = Invoke-ExternalCommand $ZstdPath $ArgList -LogPath $LogPath
|
||||
if (!$Status) {
|
||||
abort "Failed to extract files from $Path.`nLog file:`n $(friendly_path $LogPath)`n$(new_issue_msg $app $bucket 'decompress error')"
|
||||
}
|
||||
$IsTar = (strip_ext $Path) -match '\.tar$'
|
||||
if (!$IsTar -and $ExtractDir) {
|
||||
movedir (Join-Path $DestinationPath $ExtractDir) $DestinationPath | Out-Null
|
||||
}
|
||||
if (Test-Path $LogPath) {
|
||||
Remove-Item $LogPath -Force
|
||||
}
|
||||
if ($IsTar) {
|
||||
# Check for tar
|
||||
$TarFile = Join-Path $DestinationPath (strip_ext (fname $Path))
|
||||
Expand-7zipArchive -Path $TarFile -DestinationPath $DestinationPath -ExtractDir $ExtractDir -Removal
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,17 +141,17 @@ function Expand-MsiArchive {
|
||||
[Switch]
|
||||
$Removal
|
||||
)
|
||||
$DestinationPath = $DestinationPath.TrimEnd("\")
|
||||
$DestinationPath = $DestinationPath.TrimEnd('\')
|
||||
if ($ExtractDir) {
|
||||
$OriDestinationPath = $DestinationPath
|
||||
$DestinationPath = "$DestinationPath\_tmp"
|
||||
}
|
||||
if ((get_config MSIEXTRACT_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) {
|
||||
@@ -180,11 +200,11 @@ 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" }
|
||||
Default { $ArgList += "-c{app}" }
|
||||
'^[^{].*' { $ArgList += "-c{app}\$ExtractDir" }
|
||||
'^{.*' { $ArgList += "-c$ExtractDir" }
|
||||
Default { $ArgList += '-c{app}' }
|
||||
}
|
||||
if ($Switches) {
|
||||
$ArgList += (-split $Switches)
|
||||
@@ -220,34 +240,7 @@ function Expand-ZipArchive {
|
||||
$OriDestinationPath = $DestinationPath
|
||||
$DestinationPath = "$DestinationPath\_tmp"
|
||||
}
|
||||
# All methods to unzip the file require .NET4.5+
|
||||
if ($PSVersionTable.PSVersion.Major -lt 5) {
|
||||
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||
try {
|
||||
[System.IO.Compression.ZipFile]::ExtractToDirectory($Path, $DestinationPath)
|
||||
} catch [System.IO.PathTooLongException] {
|
||||
# try to fall back to 7zip if path is too long
|
||||
if (Test-HelperInstalled -Helper 7zip) {
|
||||
Expand-7zipArchive $Path $DestinationPath -Removal
|
||||
return
|
||||
} else {
|
||||
abort "Unzip failed: Windows can't handle the long paths in this zip file.`nRun 'scoop install 7zip' and try again."
|
||||
}
|
||||
} catch [System.IO.IOException] {
|
||||
if (Test-HelperInstalled -Helper 7zip) {
|
||||
Expand-7zipArchive $Path $DestinationPath -Removal
|
||||
return
|
||||
} else {
|
||||
abort "Unzip failed: Windows can't handle the file names in this zip file.`nRun 'scoop install 7zip' and try again."
|
||||
}
|
||||
} catch {
|
||||
abort "Unzip failed: $_"
|
||||
}
|
||||
} else {
|
||||
# Use Expand-Archive to unzip in PowerShell 5+
|
||||
# Compatible with Pscx (https://github.com/Pscx/Pscx)
|
||||
Microsoft.PowerShell.Archive\Expand-Archive -Path $Path -DestinationPath $DestinationPath -Force
|
||||
}
|
||||
Expand-Archive -Path $Path -DestinationPath $DestinationPath -Force
|
||||
if ($ExtractDir) {
|
||||
movedir "$DestinationPath\$ExtractDir" $OriDestinationPath | Out-Null
|
||||
Remove-Item $DestinationPath -Recurse -Force
|
||||
@@ -274,7 +267,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)
|
||||
}
|
||||
@@ -290,23 +283,3 @@ function Expand-DarkArchive {
|
||||
Remove-Item $Path -Force
|
||||
}
|
||||
}
|
||||
|
||||
function extract_7zip($path, $to, $removal) {
|
||||
Show-DeprecatedWarning $MyInvocation 'Expand-7zipArchive'
|
||||
Expand-7zipArchive -Path $path -DestinationPath $to -Removal:$removal @args
|
||||
}
|
||||
|
||||
function extract_msi($path, $to, $removal) {
|
||||
Show-DeprecatedWarning $MyInvocation 'Expand-MsiArchive'
|
||||
Expand-MsiArchive -Path $path -DestinationPath $to -Removal:$removal
|
||||
}
|
||||
|
||||
function unpack_inno($path, $to, $removal) {
|
||||
Show-DeprecatedWarning $MyInvocation 'Expand-InnoArchive'
|
||||
Expand-InnoArchive -Path $path -DestinationPath $to -Removal:$removal @args
|
||||
}
|
||||
|
||||
function extract_zip($path, $to, $removal) {
|
||||
Show-DeprecatedWarning $MyInvocation 'Expand-ZipArchive'
|
||||
Expand-ZipArchive -Path $path -DestinationPath $to -Removal:$removal
|
||||
}
|
||||
|
||||
249
lib/depends.ps1
249
lib/depends.ps1
@@ -1,98 +1,173 @@
|
||||
# resolve dependencies for the supplied apps, and sort into the correct order
|
||||
function install_order($apps, $arch) {
|
||||
$res = @()
|
||||
foreach ($app in $apps) {
|
||||
foreach ($dep in deps $app $arch) {
|
||||
if ($res -notcontains $dep) { $res += $dep}
|
||||
}
|
||||
if ($res -notcontains $app) { $res += $app }
|
||||
}
|
||||
return $res
|
||||
}
|
||||
function Get-Dependency {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Get app's dependencies (with apps attached at the end).
|
||||
.PARAMETER AppName
|
||||
App's name
|
||||
.PARAMETER Architecture
|
||||
App's architecture
|
||||
.PARAMETER Resolved
|
||||
List of resolved dependencies (internal use)
|
||||
.PARAMETER Unresolved
|
||||
List of unresolved dependencies (internal use)
|
||||
.OUTPUTS
|
||||
[Object[]]
|
||||
List of app's dependencies
|
||||
.NOTES
|
||||
When pipeline input is used, the output will have duplicate items, and should be filtered by 'Select-Object -Unique'.
|
||||
ALgorithm: http://www.electricmonk.nl/docs/dependency_resolving_algorithm/dependency_resolving_algorithm.html
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
[OutputType([Object[]])]
|
||||
param (
|
||||
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
|
||||
[PSObject]
|
||||
$AppName,
|
||||
[Parameter(Mandatory = $true, Position = 1)]
|
||||
[String]
|
||||
$Architecture,
|
||||
[String[]]
|
||||
$Resolved = @(),
|
||||
[String[]]
|
||||
$Unresolved = @()
|
||||
)
|
||||
process {
|
||||
$AppName, $manifest, $bucket, $url = Get-Manifest $AppName
|
||||
$Unresolved += $AppName
|
||||
|
||||
# http://www.electricmonk.nl/docs/dependency_resolving_algorithm/dependency_resolving_algorithm.html
|
||||
function deps($app, $arch) {
|
||||
$resolved = new-object collections.arraylist
|
||||
dep_resolve $app $arch $resolved @()
|
||||
|
||||
if ($resolved.count -eq 1) { return @() } # no dependencies
|
||||
return $resolved[0..($resolved.count - 2)]
|
||||
}
|
||||
|
||||
function dep_resolve($app, $arch, $resolved, $unresolved) {
|
||||
$app, $bucket, $null = parse_app $app
|
||||
$unresolved += $app
|
||||
$null, $manifest, $null, $null = Find-Manifest $app $bucket
|
||||
|
||||
if(!$manifest) {
|
||||
if(((Get-LocalBucket) -notcontains $bucket) -and $bucket) {
|
||||
warn "Bucket '$bucket' not installed. Add it with 'scoop bucket add $bucket' or 'scoop bucket add $bucket <repo>'."
|
||||
}
|
||||
abort "Couldn't find manifest for '$app'$(if(!$bucket) { '.' } else { " from '$bucket' bucket." })"
|
||||
}
|
||||
|
||||
$deps = @(install_deps $manifest $arch) + @(runtime_deps $manifest) | Select-Object -Unique
|
||||
|
||||
foreach ($dep in $deps) {
|
||||
if ($resolved -notcontains $dep) {
|
||||
if ($unresolved -contains $dep) {
|
||||
abort "Circular dependency detected: '$app' -> '$dep'."
|
||||
if (!$manifest) {
|
||||
if (((Get-LocalBucket) -notcontains $bucket) -and $bucket) {
|
||||
warn "Bucket '$bucket' not installed. Add it with 'scoop bucket add $bucket' or 'scoop bucket add $bucket <repo>'."
|
||||
}
|
||||
dep_resolve $dep $arch $resolved $unresolved
|
||||
abort "Couldn't find manifest for '$AppName'$(if(!$bucket) { '.' } else { " from '$bucket' bucket." })"
|
||||
}
|
||||
|
||||
$deps = @(Get-InstallationHelper $manifest $Architecture) + @($manifest.depends) | Select-Object -Unique
|
||||
|
||||
foreach ($dep in $deps) {
|
||||
if ($Resolved -notcontains $dep) {
|
||||
if ($Unresolved -contains $dep) {
|
||||
abort "Circular dependency detected: '$AppName' -> '$dep'."
|
||||
}
|
||||
$Resolved, $Unresolved = Get-Dependency $dep $Architecture -Resolved $Resolved -Unresolved $Unresolved
|
||||
}
|
||||
}
|
||||
|
||||
$Unresolved = $Unresolved -ne $AppName
|
||||
if ($bucket) {
|
||||
$Resolved += "$bucket/$AppName"
|
||||
} else {
|
||||
if ($url) {
|
||||
$Resolved += $url
|
||||
} else {
|
||||
$Resolved += $AppName
|
||||
}
|
||||
}
|
||||
if ($Unresolved.Length -eq 0) {
|
||||
return $Resolved
|
||||
} else {
|
||||
return $Resolved, $Unresolved
|
||||
}
|
||||
}
|
||||
$resolved.add($app) | Out-Null
|
||||
$unresolved = $unresolved -ne $app # remove from unresolved
|
||||
}
|
||||
|
||||
function runtime_deps($manifest) {
|
||||
if ($manifest.depends) { return $manifest.depends }
|
||||
function Get-InstallationHelper {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Get helpers that used in installation
|
||||
.PARAMETER Manifest
|
||||
App's manifest
|
||||
.PARAMETER Architecture
|
||||
Architecture of the app
|
||||
.PARAMETER All
|
||||
If true, return all helpers, otherwise return only helpers that are not already installed
|
||||
.OUTPUTS
|
||||
[Object[]]
|
||||
List of helpers
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
[OutputType([Object[]])]
|
||||
param (
|
||||
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
|
||||
[PSObject]
|
||||
$Manifest,
|
||||
[Parameter(Mandatory = $true, Position = 1)]
|
||||
[String]
|
||||
$Architecture,
|
||||
[Switch]
|
||||
$All
|
||||
)
|
||||
begin {
|
||||
$helper = @()
|
||||
}
|
||||
process {
|
||||
$url = arch_specific 'url' $Manifest $Architecture
|
||||
$pre_install = arch_specific 'pre_install' $Manifest $Architecture
|
||||
$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)) {
|
||||
$helper += '7zip'
|
||||
}
|
||||
if (((Test-LessmsiRequirement -Uri $url) -or ($script -like '*Expand-MsiArchive *')) -and (get_config MSIEXTRACT_USE_LESSMSI)) {
|
||||
$helper += 'lessmsi'
|
||||
}
|
||||
if ($Manifest.innosetup -or ($script -like '*Expand-InnoArchive *')) {
|
||||
$helper += 'innounp'
|
||||
}
|
||||
if ($script -like '*Expand-DarkArchive *') {
|
||||
$helper += 'dark'
|
||||
}
|
||||
if ((Test-ZstdRequirement -Uri $url) -or ($script -like '*Expand-ZstdArchive *')) {
|
||||
$helper += 'zstd'
|
||||
}
|
||||
if (!$All) {
|
||||
'7zip', 'lessmsi', 'innounp', 'dark', 'zstd' | ForEach-Object {
|
||||
if (Test-HelperInstalled -Helper $_) {
|
||||
$helper = $helper -ne $_
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
end {
|
||||
return $helper
|
||||
}
|
||||
}
|
||||
|
||||
function script_deps($script) {
|
||||
$deps = @()
|
||||
if($script -is [Array]) {
|
||||
$script = $script -join "`n"
|
||||
}
|
||||
if([String]::IsNullOrEmpty($script)) {
|
||||
return $deps
|
||||
}
|
||||
|
||||
if($script -like '*Expand-7zipArchive *' -or $script -like '*extract_7zip *') {
|
||||
$deps += '7zip'
|
||||
}
|
||||
if($script -like '*Expand-MsiArchive *' -or $script -like '*extract_msi *') {
|
||||
$deps += 'lessmsi'
|
||||
}
|
||||
if($script -like '*Expand-InnoArchive *' -or $script -like '*unpack_inno *') {
|
||||
$deps += 'innounp'
|
||||
}
|
||||
if($script -like '*Expand-DarkArchive *') {
|
||||
$deps += 'dark'
|
||||
}
|
||||
|
||||
return $deps
|
||||
function Test-7zipRequirement {
|
||||
[CmdletBinding()]
|
||||
[OutputType([Boolean])]
|
||||
param (
|
||||
[Parameter(Mandatory = $true)]
|
||||
[AllowNull()]
|
||||
[String[]]
|
||||
$Uri
|
||||
)
|
||||
return ($Uri | Where-Object {
|
||||
$_ -match '\.((gz)|(tar)|(t[abgpx]z2?)|(lzma)|(bz2?)|(7z)|(001)|(rar)|(iso)|(xz)|(lzh)|(nupkg))(\.[^\d.]+)?$'
|
||||
}).Count -gt 0
|
||||
}
|
||||
|
||||
function install_deps($manifest, $arch) {
|
||||
$deps = @()
|
||||
|
||||
if (!(Test-HelperInstalled -Helper 7zip) -and (Test-7zipRequirement -URL (url $manifest $arch))) {
|
||||
$deps += '7zip'
|
||||
}
|
||||
if (!(Test-HelperInstalled -Helper Lessmsi) -and (Test-LessmsiRequirement -URL (url $manifest $arch))) {
|
||||
$deps += 'lessmsi'
|
||||
}
|
||||
if (!(Test-HelperInstalled -Helper Innounp) -and $manifest.innosetup) {
|
||||
$deps += 'innounp'
|
||||
}
|
||||
|
||||
$pre_install = arch_specific 'pre_install' $manifest $arch
|
||||
$installer = arch_specific 'installer' $manifest $arch
|
||||
$post_install = arch_specific 'post_install' $manifest $arch
|
||||
$deps += script_deps $pre_install
|
||||
$deps += script_deps $installer.script
|
||||
$deps += script_deps $post_install
|
||||
|
||||
return $deps | Select-Object -Unique
|
||||
function Test-ZstdRequirement {
|
||||
[CmdletBinding()]
|
||||
[OutputType([Boolean])]
|
||||
param (
|
||||
[Parameter(Mandatory = $true)]
|
||||
[AllowNull()]
|
||||
[String[]]
|
||||
$Uri
|
||||
)
|
||||
return ($Uri | Where-Object { $_ -match '\.zst$' }).Count -gt 0
|
||||
}
|
||||
|
||||
function Test-LessmsiRequirement {
|
||||
[CmdletBinding()]
|
||||
[OutputType([Boolean])]
|
||||
param (
|
||||
[Parameter(Mandatory = $true)]
|
||||
[AllowNull()]
|
||||
[String[]]
|
||||
$Uri
|
||||
)
|
||||
return ($Uri | Where-Object { $_ -match '\.msi$' }).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,22 +3,21 @@ 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($defender -and $defender.status) {
|
||||
if($defender.status -eq [system.serviceprocess.servicecontrollerstatus]::running) {
|
||||
if (Test-CommandAvailable Get-MpPreference) {
|
||||
$defender = Get-Service -Name WinDefend -ErrorAction SilentlyContinue
|
||||
if (Test-CommandAvailable Get-MpPreference) {
|
||||
if ((Get-MpPreference).DisableRealtimeMonitoring) { return $true }
|
||||
if ($defender -and $defender.Status) {
|
||||
if ($defender.Status -eq [System.ServiceProcess.ServiceControllerStatus]::Running) {
|
||||
$installPath = $scoopdir;
|
||||
if($global) { $installPath = $globaldir; }
|
||||
if ($global) { $installPath = $globaldir; }
|
||||
|
||||
$exclusionPath = (Get-MpPreference).exclusionPath
|
||||
if(!($exclusionPath -contains $installPath)) {
|
||||
warn "Windows Defender may slow down or disrupt installs with realtime scanning."
|
||||
write-host " Consider running:"
|
||||
write-host " sudo Add-MpPreference -ExclusionPath '$installPath'"
|
||||
write-host " (Requires 'sudo' command. Run 'scoop install sudo' if you don't have it.)"
|
||||
$exclusionPath = (Get-MpPreference).ExclusionPath
|
||||
if (!($exclusionPath -contains $installPath)) {
|
||||
info "Windows Defender may slow down or disrupt installs with realtime scanning."
|
||||
Write-Host " Consider running:"
|
||||
Write-Host " sudo Add-MpPreference -ExclusionPath '$installPath'"
|
||||
Write-Host " (Requires 'sudo' command. Run 'scoop install sudo' if you don't have it.)"
|
||||
return $false
|
||||
}
|
||||
}
|
||||
@@ -28,7 +27,7 @@ function check_windows_defender($global) {
|
||||
}
|
||||
|
||||
function check_main_bucket {
|
||||
if ((Get-LocalBucket) -notcontains 'main'){
|
||||
if ((Get-LocalBucket) -notcontains 'main') {
|
||||
warn 'Main bucket is not added.'
|
||||
Write-Host " run 'scoop bucket add main'"
|
||||
|
||||
@@ -39,12 +38,16 @@ function check_main_bucket {
|
||||
}
|
||||
|
||||
function check_long_paths {
|
||||
if ([System.Environment]::OSVersion.Version.Major -lt 10 -or [System.Environment]::OSVersion.Version.Build -lt 1607) {
|
||||
warn 'This version of Windows does not support configuration of LongPaths.'
|
||||
return $false
|
||||
}
|
||||
$key = Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem' -ErrorAction SilentlyContinue -Name 'LongPathsEnabled'
|
||||
if (!$key -or ($key.LongPathsEnabled -eq 0)) {
|
||||
warn 'LongPaths support is not enabled.'
|
||||
Write-Host "You can enable it with running:"
|
||||
Write-Host " Set-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem' -Name 'LongPathsEnabled' -Value 1"
|
||||
|
||||
Write-Host " You can enable it by running:"
|
||||
Write-Host " sudo Set-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem' -Name 'LongPathsEnabled' -Value 1"
|
||||
Write-Host " (Requires 'sudo' command. Run 'scoop install sudo' if you don't have it.)"
|
||||
return $false
|
||||
}
|
||||
|
||||
|
||||
52
lib/git.ps1
52
lib/git.ps1
@@ -1,52 +0,0 @@
|
||||
function git_proxy_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"
|
||||
}
|
||||
& "$env:COMSPEC" /c $cmd
|
||||
}
|
||||
|
||||
function git_clone {
|
||||
git_proxy_cmd clone $args
|
||||
}
|
||||
|
||||
function git_ls_remote {
|
||||
git_proxy_cmd ls-remote $args
|
||||
}
|
||||
|
||||
function git_checkout {
|
||||
git_proxy_cmd checkout $args
|
||||
}
|
||||
|
||||
function git_branch {
|
||||
git_proxy_cmd branch $args
|
||||
}
|
||||
|
||||
function git_pull {
|
||||
git_proxy_cmd pull $args
|
||||
}
|
||||
|
||||
function git_fetch {
|
||||
git_proxy_cmd fetch $args
|
||||
}
|
||||
|
||||
function git_log {
|
||||
git_proxy_cmd --no-pager log $args
|
||||
}
|
||||
|
||||
function git_checkout {
|
||||
git_proxy_cmd checkout $args
|
||||
}
|
||||
|
||||
function git_branch {
|
||||
git_proxy_cmd branch $args
|
||||
}
|
||||
|
||||
function git_config {
|
||||
git_proxy_cmd config $args
|
||||
}
|
||||
|
||||
function git_reset {
|
||||
git_proxy_cmd reset $args
|
||||
}
|
||||
483
lib/install.ps1
483
lib/install.ps1
@@ -1,6 +1,3 @@
|
||||
. "$psscriptroot/autoupdate.ps1"
|
||||
. "$psscriptroot/buckets.ps1"
|
||||
|
||||
function nightly_version($date, $quiet = $false) {
|
||||
$date_str = $date.tostring("yyyyMMdd")
|
||||
if (!$quiet) {
|
||||
@@ -10,8 +7,7 @@ function nightly_version($date, $quiet = $false) {
|
||||
}
|
||||
|
||||
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" })."
|
||||
@@ -34,21 +30,34 @@ function install_app($app, $architecture, $global, $suggested, $use_cache = $tru
|
||||
return
|
||||
}
|
||||
|
||||
write-output "Installing '$app' ($version) [$architecture]"
|
||||
if ((get_config 'manifest_review' $false) -and ($MyInvocation.ScriptName -notlike '*scoop-update*')) {
|
||||
Write-Host "Manifest: $app.json"
|
||||
$style = get_config cat_style
|
||||
if ($style) {
|
||||
$manifest | ConvertToPrettyJson | bat --no-paging --style $style --language json
|
||||
} else {
|
||||
$manifest | ConvertToPrettyJson
|
||||
}
|
||||
$answer = Read-Host -Prompt "Continue installation? [Y/n]"
|
||||
if (($answer -eq 'n') -or ($answer -eq 'N')) {
|
||||
return
|
||||
}
|
||||
}
|
||||
Write-Output "Installing '$app' ($version) [$architecture]$(if ($bucket) { " from $bucket bucket" })"
|
||||
|
||||
$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
|
||||
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
|
||||
create_shims $manifest $dir $global $architecture
|
||||
create_startmenu_shortcuts $manifest $dir $global $architecture
|
||||
install_psmodule $manifest $dir $global
|
||||
if($global) { ensure_scoop_in_path $global } # can assume local scoop is in path
|
||||
env_add_path $manifest $dir $global $architecture
|
||||
env_set $manifest $dir $global $architecture
|
||||
|
||||
@@ -56,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
|
||||
@@ -71,38 +80,6 @@ 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) {
|
||||
$cached = fullpath (cache_path $app $version $url)
|
||||
|
||||
@@ -113,7 +90,11 @@ function dl_with_cache($app, $version, $url, $to, $cookies = $null, $use_cache =
|
||||
} 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,12 +181,12 @@ function get_filename_from_metalink($file) {
|
||||
|
||||
function dl_with_cache_aria2($app, $version, $manifest, $architecture, $dir, $cookies = $null, $use_cache = $true, $check_hash = $true) {
|
||||
$data = @{}
|
||||
$urls = @(url $manifest $architecture)
|
||||
$urls = @(script:url $manifest $architecture)
|
||||
|
||||
# aria2 input file
|
||||
$urlstxt = Join-Path $cachedir "$app.txt"
|
||||
$urlstxt_content = ''
|
||||
$has_downloads = $false
|
||||
$download_finished = $true
|
||||
|
||||
# aria2 options
|
||||
$options = @(
|
||||
@@ -225,127 +206,149 @@ function dl_with_cache_aria2($app, $version, $manifest, $architecture, $dir, $co
|
||||
"--min-tls-version=TLSv1.2"
|
||||
"--stop-with-process=$PID"
|
||||
"--continue"
|
||||
"--summary-interval=0"
|
||||
"--auto-save-interval=1"
|
||||
)
|
||||
|
||||
if($cookies) {
|
||||
if ($cookies) {
|
||||
$options += "--header='Cookie: $(cookie_header $cookies)'"
|
||||
}
|
||||
|
||||
$proxy = get_config 'proxy'
|
||||
if($proxy -ne 'none') {
|
||||
if([Net.Webrequest]::DefaultWebProxy.Address) {
|
||||
if ($proxy -ne 'none') {
|
||||
if ([Net.Webrequest]::DefaultWebProxy.Address) {
|
||||
$options += "--all-proxy='$([Net.Webrequest]::DefaultWebProxy.Address.Authority)'"
|
||||
}
|
||||
if([Net.Webrequest]::DefaultWebProxy.Credentials.UserName) {
|
||||
if ([Net.Webrequest]::DefaultWebProxy.Credentials.UserName) {
|
||||
$options += "--all-proxy-user='$([Net.Webrequest]::DefaultWebProxy.Credentials.UserName)'"
|
||||
}
|
||||
if([Net.Webrequest]::DefaultWebProxy.Credentials.Password) {
|
||||
if ([Net.Webrequest]::DefaultWebProxy.Credentials.Password) {
|
||||
$options += "--all-proxy-passwd='$([Net.Webrequest]::DefaultWebProxy.Credentials.Password)'"
|
||||
}
|
||||
}
|
||||
|
||||
$more_options = get_config 'aria2-options'
|
||||
if($more_options) {
|
||||
if ($more_options) {
|
||||
$options += $more_options
|
||||
}
|
||||
|
||||
foreach($url in $urls) {
|
||||
foreach ($url in $urls) {
|
||||
$data.$url = @{
|
||||
'filename' = url_filename $url
|
||||
'target' = "$dir\$(url_filename $url)"
|
||||
'target' = "$dir\$(url_filename $url)"
|
||||
'cachename' = fname (cache_path $app $version $url)
|
||||
'source' = fullpath (cache_path $app $version $url)
|
||||
'source' = fullpath (cache_path $app $version $url)
|
||||
}
|
||||
|
||||
if(!(test-path $data.$url.source)) {
|
||||
$has_downloads = $true
|
||||
if ((Test-Path $data.$url.source) -and -not((Test-Path "$($data.$url.source).aria2") -or (Test-Path $urlstxt)) -and $use_cache) {
|
||||
Write-Host 'Loading ' -NoNewline
|
||||
Write-Host $(url_remote_filename $url) -f Cyan -NoNewline
|
||||
Write-Host ' from cache.'
|
||||
} else {
|
||||
$download_finished = $false
|
||||
# create aria2 input file content
|
||||
$urlstxt_content += "$(handle_special_urls $url)`n"
|
||||
if(!$url.Contains('sourceforge.net')) {
|
||||
if (!$url.Contains('sourceforge.net')) {
|
||||
$urlstxt_content += " referer=$(strip_filename $url)`n"
|
||||
}
|
||||
$urlstxt_content += " dir=$cachedir`n"
|
||||
$urlstxt_content += " out=$($data.$url.cachename)`n"
|
||||
} else {
|
||||
Write-Host "Loading " -NoNewline
|
||||
Write-Host $(url_remote_filename $url) -f Cyan -NoNewline
|
||||
Write-Host " from cache."
|
||||
}
|
||||
}
|
||||
|
||||
if($has_downloads) {
|
||||
if (-not($download_finished)) {
|
||||
# write aria2 input file
|
||||
Set-Content -Path $urlstxt $urlstxt_content
|
||||
if ($urlstxt_content -ne '') {
|
||||
ensure $cachedir | Out-Null
|
||||
# Write aria2 input-file with UTF8NoBOM encoding
|
||||
$urlstxt_content | Out-UTF8File -FilePath $urlstxt
|
||||
}
|
||||
|
||||
# build aria2 command
|
||||
$aria2 = "& '$(Get-HelperPath -Helper Aria2)' $($options -join ' ')"
|
||||
|
||||
# handle aria2 console output
|
||||
Write-Host "Starting download with aria2 ..."
|
||||
$prefix = "Download: "
|
||||
Invoke-Expression $aria2 | ForEach-Object {
|
||||
if([String]::IsNullOrWhiteSpace($_)) {
|
||||
# skip blank lines
|
||||
return
|
||||
}
|
||||
Write-Host $prefix -NoNewline
|
||||
if($_.StartsWith('(OK):')) {
|
||||
Write-Host $_ -f Green
|
||||
} elseif($_.StartsWith('[') -and $_.EndsWith(']')) {
|
||||
Write-Host $_ -f Cyan
|
||||
} else {
|
||||
Write-Host $_ -f Gray
|
||||
Write-Host 'Starting download with aria2 ...'
|
||||
|
||||
# Set console output encoding to UTF8 for non-ASCII characters printing
|
||||
$oriConsoleEncoding = [Console]::OutputEncoding
|
||||
[Console]::OutputEncoding = New-Object System.Text.UTF8Encoding
|
||||
|
||||
Invoke-Command ([scriptblock]::Create($aria2)) | ForEach-Object {
|
||||
# Skip blank lines
|
||||
if ([String]::IsNullOrWhiteSpace($_)) { return }
|
||||
|
||||
# Prevent potential overlaping of text when one line is shorter
|
||||
$len = $Host.UI.RawUI.WindowSize.Width - $_.Length - 20
|
||||
$blank = if ($len -gt 0) { ' ' * $len } else { '' }
|
||||
$color = 'Gray'
|
||||
|
||||
if ($_.StartsWith('(OK):')) {
|
||||
$noNewLine = $true
|
||||
$color = 'Green'
|
||||
} elseif ($_.StartsWith('[') -and $_.EndsWith(']')) {
|
||||
$noNewLine = $true
|
||||
$color = 'Cyan'
|
||||
} elseif ($_.StartsWith('Download Results:')) {
|
||||
$noNewLine = $false
|
||||
}
|
||||
|
||||
Write-Host "`rDownload: $_$blank" -ForegroundColor $color -NoNewline:$noNewLine
|
||||
}
|
||||
Write-Host ''
|
||||
|
||||
if($lastexitcode -gt 0) {
|
||||
error "Download failed! (Error $lastexitcode) $(aria_exit_code $lastexitcode)"
|
||||
error $urlstxt_content
|
||||
error $aria2
|
||||
abort $(new_issue_msg $app $bucket "download via aria2 failed")
|
||||
abort $(new_issue_msg $app $bucket 'download via aria2 failed')
|
||||
}
|
||||
|
||||
# remove aria2 input file when done
|
||||
if(test-path($urlstxt)) {
|
||||
Remove-Item $urlstxt
|
||||
if (Test-Path $urlstxt, "$($data.$url.source).aria2*") {
|
||||
Remove-Item $urlstxt -Force -ErrorAction SilentlyContinue
|
||||
Remove-Item "$($data.$url.source).aria2*" -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
# Revert console encoding
|
||||
[Console]::OutputEncoding = $oriConsoleEncoding
|
||||
}
|
||||
|
||||
foreach($url in $urls) {
|
||||
foreach ($url in $urls) {
|
||||
|
||||
$metalink_filename = get_filename_from_metalink $data.$url.source
|
||||
if($metalink_filename) {
|
||||
if ($metalink_filename) {
|
||||
Remove-Item $data.$url.source -Force
|
||||
Rename-Item -Force (Join-Path -Path $cachedir -ChildPath $metalink_filename) $data.$url.source
|
||||
}
|
||||
|
||||
# run hash checks
|
||||
if($check_hash) {
|
||||
if ($check_hash) {
|
||||
$manifest_hash = hash_for_url $manifest $url $architecture
|
||||
$ok, $err = check_hash $data.$url.source $manifest_hash $(show_app $app $bucket)
|
||||
if(!$ok) {
|
||||
if (!$ok) {
|
||||
error $err
|
||||
if(test-path $data.$url.source) {
|
||||
if (Test-Path $data.$url.source) {
|
||||
# rm cached file
|
||||
Remove-Item -force $data.$url.source
|
||||
Remove-Item $data.$url.source -Force -ErrorAction SilentlyContinue
|
||||
Remove-Item "$($data.$url.source).aria2*" -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
if($url.Contains('sourceforge.net')) {
|
||||
if ($url.Contains('sourceforge.net')) {
|
||||
Write-Host -f yellow 'SourceForge.net is known for causing hash validation fails. Please try again before opening a ticket.'
|
||||
}
|
||||
abort $(new_issue_msg $app $bucket "hash check failed")
|
||||
abort $(new_issue_msg $app $bucket 'hash check failed')
|
||||
}
|
||||
}
|
||||
|
||||
# copy or move file to target location
|
||||
if(!(test-path $data.$url.source) ) {
|
||||
abort $(new_issue_msg $app $bucket "cached file not found")
|
||||
if (!(Test-Path $data.$url.source) ) {
|
||||
abort $(new_issue_msg $app $bucket 'cached file not found')
|
||||
}
|
||||
|
||||
if(!($dir -eq $cachedir)) {
|
||||
if($use_cache) {
|
||||
if (!($dir -eq $cachedir)) {
|
||||
if ($use_cache) {
|
||||
Copy-Item $data.$url.source $data.$url.target
|
||||
} else {
|
||||
Move-Item $data.$url.source $data.$url.target -force
|
||||
Move-Item $data.$url.source $data.$url.target -Force
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -353,19 +356,63 @@ 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")) {
|
||||
$wreq.referer = strip_filename $url
|
||||
$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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$wres = $wreq.getresponse()
|
||||
try {
|
||||
$wres = $wreq.GetResponse()
|
||||
} catch [System.Net.WebException] {
|
||||
$exc = $_.Exception
|
||||
$handledCodes = @(
|
||||
[System.Net.HttpStatusCode]::MovedPermanently, # HTTP 301
|
||||
[System.Net.HttpStatusCode]::Found, # HTTP 302
|
||||
[System.Net.HttpStatusCode]::SeeOther, # HTTP 303
|
||||
[System.Net.HttpStatusCode]::TemporaryRedirect # HTTP 307
|
||||
)
|
||||
|
||||
# Only handle redirection codes
|
||||
$redirectRes = $exc.Response
|
||||
if ($handledCodes -notcontains $redirectRes.StatusCode) {
|
||||
throw $exc
|
||||
}
|
||||
|
||||
# Get the new location of the file
|
||||
if ((-not $redirectRes.Headers) -or ($redirectRes.Headers -notcontains 'Location')) {
|
||||
throw $exc
|
||||
}
|
||||
|
||||
$newUrl = $redirectRes.Headers['Location']
|
||||
info "Following redirect to $newUrl..."
|
||||
|
||||
# Handle manual file rename
|
||||
if ($url -like '*#/*') {
|
||||
$null, $postfix = $url -split '#/'
|
||||
$newUrl = "$newUrl#/$postfix"
|
||||
}
|
||||
|
||||
dl $newUrl $to $cookies $progress
|
||||
return
|
||||
}
|
||||
|
||||
$total = $wres.ContentLength
|
||||
if($total -eq -1 -and $wreq -is [net.ftpwebrequest]) {
|
||||
$total = ftp_file_size($url)
|
||||
@@ -469,6 +516,7 @@ function dl_progress($read, $total, $url) {
|
||||
write-host
|
||||
$left = 0
|
||||
$top = $top + 1
|
||||
if($top -gt $console.CursorPosition.Y) { $top = $console.CursorPosition.Y }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -482,7 +530,7 @@ function dl_urls($app, $version, $manifest, $bucket, $architecture, $dir, $use_c
|
||||
|
||||
# can be multiple urls: if there are, then msi or installer should go last,
|
||||
# so that $fname is set properly
|
||||
$urls = @(url $manifest $architecture)
|
||||
$urls = @(script:url $manifest $architecture)
|
||||
|
||||
# can be multiple cookies: they will be used for all HTTP requests.
|
||||
$cookies = $manifest.cookie
|
||||
@@ -552,7 +600,9 @@ function dl_urls($app, $version, $manifest, $bucket, $architecture, $dir, $use_c
|
||||
} else {
|
||||
$extract_fn = 'Expand-MsiArchive'
|
||||
}
|
||||
} elseif(Test-7zipRequirement -File $fname) { # 7zip
|
||||
} elseif(Test-ZstdRequirement -Uri $fname) { # Zstd first
|
||||
$extract_fn = 'Expand-ZstdArchive'
|
||||
} elseif(Test-7zipRequirement -Uri $fname) { # 7zip
|
||||
$extract_fn = 'Expand-7zipArchive'
|
||||
}
|
||||
|
||||
@@ -597,7 +647,7 @@ function hash_for_url($manifest, $url, $arch) {
|
||||
|
||||
if($hashes.length -eq 0) { return $null }
|
||||
|
||||
$urls = @(url $manifest $arch)
|
||||
$urls = @(script:url $manifest $arch)
|
||||
|
||||
$index = [array]::indexof($urls, $url)
|
||||
if($index -eq -1) { abort "Couldn't find hash in manifest for '$url'." }
|
||||
@@ -672,7 +722,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
|
||||
}
|
||||
|
||||
@@ -752,7 +802,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
|
||||
}
|
||||
|
||||
@@ -819,66 +869,63 @@ function create_shims($manifest, $dir, $global, $arch) {
|
||||
}
|
||||
}
|
||||
|
||||
function rm_shim($name, $shimdir) {
|
||||
$shim = "$shimdir\$name.ps1"
|
||||
|
||||
if(!(test-path $shim)) { # handle no shim from failed install
|
||||
warn "Shim for '$name' is missing. Skipping."
|
||||
} else {
|
||||
write-output "Removing shim for '$name'."
|
||||
Remove-Item $shim
|
||||
}
|
||||
|
||||
# other shim types might be present
|
||||
'', '.exe', '.shim', '.cmd' | ForEach-Object {
|
||||
if(test-path -Path "$shimdir\$name$_" -PathType leaf) {
|
||||
Remove-Item "$shimdir\$name$_"
|
||||
function rm_shim($name, $shimdir, $app) {
|
||||
'', '.shim', '.cmd', '.ps1' | ForEach-Object {
|
||||
$shimPath = "$shimdir\$name$_"
|
||||
$altShimPath = "$shimPath.$app"
|
||||
if ($app -and (Test-Path -Path $altShimPath -PathType Leaf)) {
|
||||
Write-Output "Removing shim '$name$_.$app'."
|
||||
Remove-Item $altShimPath
|
||||
} elseif (Test-Path -Path $shimPath -PathType Leaf) {
|
||||
Write-Output "Removing shim '$name$_'."
|
||||
Remove-Item $shimPath
|
||||
$oldShims = Get-Item -Path "$shimPath.*" -Exclude '*.shim', '*.cmd', '*.ps1'
|
||||
if ($null -eq $oldShims) {
|
||||
if ($_ -eq '.shim') {
|
||||
Write-Output "Removing shim '$name.exe'."
|
||||
Remove-Item -Path "$shimdir\$name.exe"
|
||||
}
|
||||
} else {
|
||||
(@($oldShims) | Sort-Object -Property LastWriteTimeUtc)[-1] | Rename-Item -NewName { $_.Name -replace '\.[^.]*$', '' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function rm_shims($manifest, $global, $arch) {
|
||||
function rm_shims($app, $manifest, $global, $arch) {
|
||||
$shims = @(arch_specific 'bin' $manifest $arch)
|
||||
|
||||
$shims | Where-Object { $_ -ne $null } | ForEach-Object {
|
||||
$target, $name, $null = shim_def $_
|
||||
$shimdir = shimdir $global
|
||||
|
||||
rm_shim $name $shimdir
|
||||
rm_shim $name $shimdir $app
|
||||
}
|
||||
}
|
||||
|
||||
# Gets the path for the 'current' directory junction for
|
||||
# the specified version directory.
|
||||
function current_dir($versiondir) {
|
||||
$parent = split-path $versiondir
|
||||
return "$parent\current"
|
||||
}
|
||||
|
||||
|
||||
# Creates or updates the directory junction for [app]/current,
|
||||
# pointing to the specified version directory for the app.
|
||||
#
|
||||
# Returns the 'current' junction directory if in use, otherwise
|
||||
# the version directory.
|
||||
function link_current($versiondir) {
|
||||
if(get_config NO_JUNCTIONS) { return $versiondir }
|
||||
if (get_config NO_JUNCTIONS) { return $versiondir.ToString() }
|
||||
|
||||
$currentdir = current_dir $versiondir
|
||||
$currentdir = "$(Split-Path $versiondir)\current"
|
||||
|
||||
write-host "Linking $(friendly_path $currentdir) => $(friendly_path $versiondir)"
|
||||
Write-Host "Linking $(friendly_path $currentdir) => $(friendly_path $versiondir)"
|
||||
|
||||
if($currentdir -eq $versiondir) {
|
||||
if ($currentdir -eq $versiondir) {
|
||||
abort "Error: Version 'current' is not allowed!"
|
||||
}
|
||||
|
||||
if(test-path $currentdir) {
|
||||
if (Test-Path $currentdir) {
|
||||
# remove the junction
|
||||
attrib -R /L $currentdir
|
||||
& "$env:COMSPEC" /c rmdir $currentdir
|
||||
Remove-Item $currentdir -Recurse -Force -ErrorAction Stop
|
||||
}
|
||||
|
||||
& "$env:COMSPEC" /c mklink /j $currentdir $versiondir | out-null
|
||||
New-DirectoryJunction $currentdir $versiondir | Out-Null
|
||||
attrib $currentdir +R /L
|
||||
return $currentdir
|
||||
}
|
||||
@@ -889,17 +936,17 @@ 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 }
|
||||
$currentdir = current_dir $versiondir
|
||||
if (get_config NO_JUNCTIONS) { return $versiondir.ToString() }
|
||||
$currentdir = "$(Split-Path $versiondir)\current"
|
||||
|
||||
if(test-path $currentdir) {
|
||||
write-host "Unlinking $(friendly_path $currentdir)"
|
||||
if (Test-Path $currentdir) {
|
||||
Write-Host "Unlinking $(friendly_path $currentdir)"
|
||||
|
||||
# remove read-only attribute on link
|
||||
attrib $currentdir -R /L
|
||||
|
||||
# remove the junction
|
||||
& "$env:COMSPEC" /c "rmdir `"$currentdir`""
|
||||
Remove-Item $currentdir -Recurse -Force -ErrorAction Stop
|
||||
return $currentdir
|
||||
}
|
||||
return $versiondir
|
||||
@@ -938,13 +985,22 @@ function find_dir_or_subdir($path, $dir) {
|
||||
|
||||
function env_add_path($manifest, $dir, $global, $arch) {
|
||||
$env_add_path = arch_specific 'env_add_path' $manifest $arch
|
||||
$env_add_path | Where-Object { $_ } | ForEach-Object {
|
||||
$path_dir = Join-Path $dir $_
|
||||
$dir = $dir.TrimEnd('\')
|
||||
if ($env_add_path) {
|
||||
# GH-3785: Add path in ascending order.
|
||||
[Array]::Reverse($env_add_path)
|
||||
$env_add_path | Where-Object { $_ } | ForEach-Object {
|
||||
if ($_ -eq '.') {
|
||||
$path_dir = $dir
|
||||
} else {
|
||||
$path_dir = Join-Path $dir $_
|
||||
}
|
||||
|
||||
if (!(is_in_dir $dir $path_dir)) {
|
||||
abort "Error in manifest: env_add_path '$_' is outside the app directory."
|
||||
if (!(is_in_dir $dir $path_dir)) {
|
||||
abort "Error in manifest: env_add_path '$_' is outside the app directory."
|
||||
}
|
||||
add_first_in_path $path_dir $global
|
||||
}
|
||||
add_first_in_path $path_dir $global
|
||||
}
|
||||
}
|
||||
|
||||
@@ -979,19 +1035,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')]
|
||||
[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"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1019,19 +1081,19 @@ function prune_installed($apps, $global) {
|
||||
return @($uninstalled), @($installed)
|
||||
}
|
||||
|
||||
# check whether the app failed to install
|
||||
function failed($app, $global) {
|
||||
$ver = current_version $app $global
|
||||
if(!$ver) { return $false }
|
||||
$info = install_info $app $ver $global
|
||||
if(!$info) { return $true }
|
||||
return $false
|
||||
}
|
||||
|
||||
function ensure_none_failed($apps, $global) {
|
||||
foreach($app in $apps) {
|
||||
if(failed $app $global) {
|
||||
abort "'$app' install failed previously. Please uninstall it and try again."
|
||||
function ensure_none_failed($apps) {
|
||||
foreach ($app in $apps) {
|
||||
$app = ($app -split '/|\\')[-1] -replace '\.json$', ''
|
||||
foreach ($global in $true, $false) {
|
||||
if (failed $app $global) {
|
||||
if (installed $app $global) {
|
||||
info "Repair previous failed installation of $app."
|
||||
& "$PSScriptRoot\..\libexec\scoop-reset.ps1" $app$(if ($global) { ' --global' })
|
||||
} else {
|
||||
warn "Purging previous failed installation of $app."
|
||||
& "$PSScriptRoot\..\libexec\scoop-uninstall.ps1" $app$(if ($global) { ' --global' })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1103,15 +1165,15 @@ function persist_data($manifest, $original_dir, $persist_dir) {
|
||||
if (Test-Path $source) {
|
||||
Move-Item -Force $source "$source.original"
|
||||
}
|
||||
# we don't have persist data in the store, move the source to target, then create link
|
||||
# we don't have persist data in the store, move the source to target, then create link
|
||||
} elseif (Test-Path $source) {
|
||||
# ensure target parent folder exist
|
||||
ensure (Split-Path -Path $target) | Out-Null
|
||||
Move-Item $source $target
|
||||
# we don't have neither source nor target data! we need to crate an empty target,
|
||||
# but we can't make a judgement that the data should be a file or directory...
|
||||
# so we create a directory by default. to avoid this, use pre_install
|
||||
# to create the source file before persisting (DON'T use post_install)
|
||||
# we don't have neither source nor target data! we need to create an empty target,
|
||||
# but we can't make a judgement that the data should be a file or directory...
|
||||
# so we create a directory by default. to avoid this, use pre_install
|
||||
# to create the source file before persisting (DON'T use post_install)
|
||||
} else {
|
||||
$target = New-Object System.IO.DirectoryInfo($target)
|
||||
ensure $target | Out-Null
|
||||
@@ -1120,31 +1182,35 @@ function persist_data($manifest, $original_dir, $persist_dir) {
|
||||
# create link
|
||||
if (is_directory $target) {
|
||||
# target is a directory, create junction
|
||||
& "$env:COMSPEC" /c "mklink /j `"$source`" `"$target`"" | out-null
|
||||
New-DirectoryJunction $source $target | Out-Null
|
||||
attrib $source +R /L
|
||||
} else {
|
||||
# target is a file, create hard link
|
||||
& "$env:COMSPEC" /c "mklink /h `"$source`" `"$target`"" | out-null
|
||||
New-Item -Path $source -ItemType HardLink -Value $target | Out-Null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function unlink_persist_data($dir) {
|
||||
function unlink_persist_data($manifest, $dir) {
|
||||
$persist = $manifest.persist
|
||||
# unlink all junction / hard link in the directory
|
||||
Get-ChildItem -Recurse $dir | ForEach-Object {
|
||||
$file = $_
|
||||
if ($null -ne $file.LinkType) {
|
||||
$filepath = $file.FullName
|
||||
# directory (junction)
|
||||
if ($file -is [System.IO.DirectoryInfo]) {
|
||||
# remove read-only attribute on the link
|
||||
attrib -R /L $filepath
|
||||
# remove the junction
|
||||
& "$env:COMSPEC" /c "rmdir /s /q `"$filepath`""
|
||||
} else {
|
||||
# remove the hard link
|
||||
& "$env:COMSPEC" /c "del `"$filepath`""
|
||||
if ($persist) {
|
||||
@($persist) | ForEach-Object {
|
||||
$source, $null = persist_def $_
|
||||
$source = Get-Item "$dir\$source"
|
||||
if ($source.LinkType) {
|
||||
$source_path = $source.FullName
|
||||
# directory (junction)
|
||||
if ($source -is [System.IO.DirectoryInfo]) {
|
||||
# remove read-only attribute on the link
|
||||
attrib -R /L $source_path
|
||||
# remove the junction
|
||||
Remove-Item -Path $source_path -Recurse -Force -ErrorAction SilentlyContinue
|
||||
} else {
|
||||
# remove the hard link
|
||||
Remove-Item -Path $source_path -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1161,3 +1227,32 @@ function persist_permission($manifest, $global) {
|
||||
$acl | Set-Acl -Path $path
|
||||
}
|
||||
}
|
||||
|
||||
# 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\*" }
|
||||
|
||||
if ($running_processes) {
|
||||
if (get_config 'ignore_running_processes') {
|
||||
warn "Application `"$app`" is still running. Scoop is configured to ignore this condition."
|
||||
return $false
|
||||
} else {
|
||||
error "Application `"$app`" is still running. Close all instances and try again."
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
42
lib/json.ps1
42
lib/json.ps1
@@ -92,32 +92,34 @@ function ConvertToPrettyJson {
|
||||
}
|
||||
}
|
||||
|
||||
function json_path([String] $json, [String] $jsonpath, [Hashtable] $substitutions) {
|
||||
Add-Type -Path "$psscriptroot\..\supporting\validator\bin\Newtonsoft.Json.dll"
|
||||
function json_path([String] $json, [String] $jsonpath, [Hashtable] $substitutions, [Boolean] $reverse, [Boolean] $single) {
|
||||
Add-Type -Path "$PSScriptRoot\..\supporting\validator\bin\Newtonsoft.Json.dll"
|
||||
if ($null -ne $substitutions) {
|
||||
$jsonpath = substitute $jsonpath $substitutions ($jsonpath -like "*=~*")
|
||||
}
|
||||
try {
|
||||
$obj = [Newtonsoft.Json.Linq.JObject]::Parse($json)
|
||||
$obj = [Newtonsoft.Json.Linq.JValue]::Parse($json)
|
||||
} catch [Newtonsoft.Json.JsonReaderException] {
|
||||
try {
|
||||
$obj = [Newtonsoft.Json.Linq.JArray]::Parse($json)
|
||||
} catch [Newtonsoft.Json.JsonReaderException] {
|
||||
return $null
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
try {
|
||||
$result = $obj.SelectToken($jsonpath, $true)
|
||||
} catch [Newtonsoft.Json.JsonException] {
|
||||
return $null
|
||||
}
|
||||
return $result.ToString()
|
||||
} catch [System.Management.Automation.MethodInvocationException] {
|
||||
write-host -f DarkRed $_
|
||||
return $null
|
||||
}
|
||||
try {
|
||||
$result = $obj.SelectTokens($jsonpath, $true)
|
||||
if ($reverse) {
|
||||
# Return versions in reverse order
|
||||
$result = [System.Linq.Enumerable]::Reverse($result)
|
||||
}
|
||||
if ($single) {
|
||||
# Extract First value
|
||||
$result = [System.Linq.Enumerable]::First($result)
|
||||
# Convert first value to string
|
||||
$result = $result.ToString()
|
||||
} else {
|
||||
$result = "$([String]::Join('\n', $result))"
|
||||
}
|
||||
return $result
|
||||
} catch [Exception] {
|
||||
Write-Host $_ -ForegroundColor DarkRed
|
||||
}
|
||||
|
||||
return $null
|
||||
}
|
||||
@@ -160,7 +162,7 @@ function normalize_values([psobject] $json) {
|
||||
# Recursively edit psobjects
|
||||
# If the values is psobjects, its not normalized
|
||||
# For example if manifest have architecture and it's architecture have array with single value it's not formatted.
|
||||
# @see https://github.com/lukesampson/scoop/pull/2642#issue-220506263
|
||||
# @see https://github.com/ScoopInstaller/Scoop/pull/2642#issue-220506263
|
||||
if ($_.Value -is [System.Management.Automation.PSCustomObject]) {
|
||||
$_.Value = normalize_values $_.Value
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
. "$psscriptroot/core.ps1"
|
||||
. "$psscriptroot/autoupdate.ps1"
|
||||
|
||||
function manifest_path($app, $bucket) {
|
||||
fullpath "$(Find-BucketDirectory $bucket)\$(sanitary_path $app).json"
|
||||
}
|
||||
|
||||
function parse_json($path) {
|
||||
if(!(test-path $path)) { return $null }
|
||||
Get-Content $path -raw -Encoding UTF8 | convertfrom-json -ea stop
|
||||
if (!(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,28 +92,40 @@ 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 {
|
||||
if([intptr]::size -eq 8) { return "64bit" }
|
||||
"32bit"
|
||||
$arch = get_config 'default_architecture'
|
||||
$system = if ([Environment]::Is64BitOperatingSystem) { '64bit' } else { '32bit' }
|
||||
if ($null -eq $arch) {
|
||||
$arch = $system
|
||||
} else {
|
||||
try {
|
||||
$arch = ensure_architecture $arch
|
||||
} catch {
|
||||
warn 'Invalid default architecture configured. Determining default system architecture'
|
||||
$arch = $system
|
||||
}
|
||||
}
|
||||
|
||||
return $arch
|
||||
}
|
||||
|
||||
function arch_specific($prop, $manifest, $architecture) {
|
||||
if($manifest.architecture) {
|
||||
if ($manifest.architecture) {
|
||||
$val = $manifest.architecture.$architecture.$prop
|
||||
if($val) { return $val } # else fallback to generic prop
|
||||
if ($val) { return $val } # else fallback to generic prop
|
||||
}
|
||||
|
||||
if($manifest.$prop) { return $manifest.$prop }
|
||||
if ($manifest.$prop) { return $manifest.$prop }
|
||||
}
|
||||
|
||||
function supports_architecture($manifest, $architecture) {
|
||||
@@ -77,7 +133,8 @@ function supports_architecture($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
|
||||
}
|
||||
@@ -90,7 +147,7 @@ function generate_user_manifest($app, $bucket, $version) {
|
||||
|
||||
ensure $(usermanifestsdir) | out-null
|
||||
try {
|
||||
autoupdate $app "$(resolve-path $(usermanifestsdir))" $manifest $version $(@{})
|
||||
Invoke-AutoUpdate $app "$(resolve-path $(usermanifestsdir))" $manifest $version $(@{ })
|
||||
return "$(resolve-path $(usermanifest $app))"
|
||||
} catch {
|
||||
write-host -f darkred "Could not install $app@$version"
|
||||
|
||||
@@ -2,56 +2,56 @@ $modulesdir = "$scoopdir\modules"
|
||||
|
||||
function install_psmodule($manifest, $dir, $global) {
|
||||
$psmodule = $manifest.psmodule
|
||||
if(!$psmodule) { return }
|
||||
if (!$psmodule) { return }
|
||||
|
||||
if($global) {
|
||||
abort "Installing PowerShell modules globally is not implemented!"
|
||||
if ($global) {
|
||||
abort 'Installing PowerShell modules globally is not implemented!'
|
||||
}
|
||||
|
||||
$modulesdir = ensure $modulesdir
|
||||
ensure_in_psmodulepath $modulesdir $global
|
||||
|
||||
$module_name = $psmodule.name
|
||||
if(!$module_name) {
|
||||
if (!$module_name) {
|
||||
abort "Invalid manifest: The 'name' property is missing from 'psmodule'."
|
||||
}
|
||||
|
||||
$linkfrom = "$modulesdir\$module_name"
|
||||
write-host "Installing PowerShell module '$module_name'"
|
||||
Write-Host "Installing PowerShell module '$module_name'"
|
||||
|
||||
write-host "Linking $(friendly_path $linkfrom) => $(friendly_path $dir)"
|
||||
Write-Host "Linking $(friendly_path $linkfrom) => $(friendly_path $dir)"
|
||||
|
||||
if(test-path $linkfrom) {
|
||||
if (Test-Path $linkfrom) {
|
||||
warn "$(friendly_path $linkfrom) already exists. It will be replaced."
|
||||
& "$env:COMSPEC" /c "rmdir `"$linkfrom`""
|
||||
Remove-Item -Path $linkfrom -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
& "$env:COMSPEC" /c "mklink /j `"$linkfrom`" `"$dir`"" | out-null
|
||||
New-DirectoryJunction $linkfrom $dir | Out-Null
|
||||
}
|
||||
|
||||
function uninstall_psmodule($manifest, $dir, $global) {
|
||||
$psmodule = $manifest.psmodule
|
||||
if(!$psmodule) { return }
|
||||
if (!$psmodule) { return }
|
||||
|
||||
$module_name = $psmodule.name
|
||||
write-host "Uninstalling PowerShell module '$module_name'."
|
||||
Write-Host "Uninstalling PowerShell module '$module_name'."
|
||||
|
||||
$linkfrom = "$modulesdir\$module_name"
|
||||
if(test-path $linkfrom) {
|
||||
write-host "Removing $(friendly_path $linkfrom)"
|
||||
$linkfrom = resolve-path $linkfrom
|
||||
& "$env:COMSPEC" /c "rmdir `"$linkfrom`""
|
||||
if (Test-Path $linkfrom) {
|
||||
Write-Host "Removing $(friendly_path $linkfrom)"
|
||||
$linkfrom = Resolve-Path $linkfrom
|
||||
Remove-Item -Path $linkfrom -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
function ensure_in_psmodulepath($dir, $global) {
|
||||
$path = env 'psmodulepath' $global
|
||||
if(!$global -and $null -eq $path) {
|
||||
if (!$global -and $null -eq $path) {
|
||||
$path = "$env:USERPROFILE\Documents\WindowsPowerShell\Modules"
|
||||
}
|
||||
$dir = fullpath $dir
|
||||
if($path -notmatch [regex]::escape($dir)) {
|
||||
write-output "Adding $(friendly_path $dir) to $(if($global){'global'}else{'your'}) PowerShell module path."
|
||||
if ($path -notmatch [Regex]::Escape($dir)) {
|
||||
Write-Output "Adding $(friendly_path $dir) to $(if($global){'global'}else{'your'}) PowerShell module path."
|
||||
|
||||
env 'psmodulepath' $global "$dir;$path" # for future sessions...
|
||||
$env:psmodulepath = "$dir;$env:psmodulepath" # for this session
|
||||
|
||||
@@ -20,11 +20,12 @@ function create_startmenu_shortcuts($manifest, $dir, $global, $arch) {
|
||||
}
|
||||
|
||||
function shortcut_folder($global) {
|
||||
$directory = [System.IO.Path]::Combine([Environment]::GetFolderPath('startmenu'), 'Programs', 'Scoop Apps')
|
||||
if($global) {
|
||||
$directory = [System.IO.Path]::Combine([Environment]::GetFolderPath('commonstartmenu'), 'Programs', 'Scoop Apps')
|
||||
if ($global) {
|
||||
$startmenu = 'CommonStartMenu'
|
||||
} else {
|
||||
$startmenu = 'StartMenu'
|
||||
}
|
||||
return $(ensure $directory)
|
||||
return Convert-Path (ensure ([System.IO.Path]::Combine([Environment]::GetFolderPath($startmenu), 'Programs', 'Scoop Apps')))
|
||||
}
|
||||
|
||||
function startmenu_shortcut([System.IO.FileInfo] $target, $shortcutName, $arguments, [System.IO.FileInfo]$icon, $global) {
|
||||
@@ -67,18 +68,5 @@ function rm_startmenu_shortcuts($manifest, $global, $arch) {
|
||||
if(Test-Path -Path $shortcut) {
|
||||
Remove-Item $shortcut
|
||||
}
|
||||
# Before issue 1514 Startmenu shortcut removal
|
||||
#
|
||||
# Shortcuts that should have been installed globally would
|
||||
# have been installed locally up until 27 June 2017.
|
||||
#
|
||||
# TODO: Remove this 'if' block and comment after
|
||||
# 27 June 2018.
|
||||
if($global) {
|
||||
$shortcut = "$(shortcut_folder $false)\$name.lnk"
|
||||
if(Test-Path -Path $shortcut) {
|
||||
Remove-Item $shortcut
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
304
lib/versions.ps1
304
lib/versions.ps1
@@ -1,53 +1,285 @@
|
||||
# versions
|
||||
function latest_version($app, $bucket, $url) {
|
||||
(manifest $app $bucket $url).version
|
||||
}
|
||||
function current_version($app, $global) {
|
||||
@(versions $app $global)[-1]
|
||||
}
|
||||
function versions($app, $global) {
|
||||
$appdir = appdir $app $global
|
||||
if(!(test-path $appdir)) { return @() }
|
||||
|
||||
sort_versions (Get-ChildItem $appdir -dir -attr !reparsePoint | Where-Object { $null -ne $(Get-ChildItem $_.fullname) } | ForEach-Object { $_.name })
|
||||
}
|
||||
|
||||
function version($ver) {
|
||||
$ver -split '[\.-]' | ForEach-Object {
|
||||
$num = $_ -as [int]
|
||||
if($num) { $num } else { $_ }
|
||||
function Get-LatestVersion {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Get latest version of app from manifest
|
||||
.PARAMETER AppName
|
||||
App's name
|
||||
.PARAMETER Bucket
|
||||
Bucket which the app belongs to
|
||||
.PARAMETER Uri
|
||||
Remote app manifest's URI
|
||||
#>
|
||||
[OutputType([String])]
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
|
||||
[Alias('App')]
|
||||
[String]
|
||||
$AppName,
|
||||
[Parameter(Position = 1)]
|
||||
[String]
|
||||
$Bucket,
|
||||
[Parameter(Position = 2)]
|
||||
[String]
|
||||
$Uri
|
||||
)
|
||||
process {
|
||||
return (manifest $AppName $Bucket $Uri).version
|
||||
}
|
||||
}
|
||||
function compare_versions($a, $b) {
|
||||
$ver_a = @(version $a)
|
||||
$ver_b = @(version $b)
|
||||
|
||||
for($i=0;$i -lt $ver_a.length;$i++) {
|
||||
if($i -gt $ver_b.length) { return 1; }
|
||||
function Select-CurrentVersion { # 'manifest.ps1'
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Select current version of installed app, from 'current\manifest.json' or modified time of version directory
|
||||
.PARAMETER AppName
|
||||
App's name
|
||||
.PARAMETER Global
|
||||
Globally installed application
|
||||
#>
|
||||
[OutputType([String])]
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
|
||||
[Alias('App')]
|
||||
[String]
|
||||
$AppName,
|
||||
[Parameter(Position = 1)]
|
||||
[Switch]
|
||||
$Global
|
||||
)
|
||||
process {
|
||||
$currentPath = "$(appdir $AppName $Global)\current"
|
||||
if (!(get_config NO_JUNCTIONS)) {
|
||||
$currentVersion = (parse_json "$currentPath\manifest.json").version
|
||||
if ($currentVersion -eq 'nightly') {
|
||||
$currentVersion = (Get-Item $currentPath).Target | Split-Path -Leaf
|
||||
}
|
||||
}
|
||||
if ($null -eq $currentVersion) {
|
||||
$installedVersion = Get-InstalledVersion -AppName $AppName -Global:$Global
|
||||
if ($installedVersion) {
|
||||
$currentVersion = @($installedVersion)[-1]
|
||||
} else {
|
||||
$currentVersion = $null
|
||||
}
|
||||
}
|
||||
return $currentVersion
|
||||
}
|
||||
}
|
||||
|
||||
# don't try to compare int to string
|
||||
if($ver_b[$i] -is [string] -and $ver_a[$i] -isnot [string]) {
|
||||
$ver_a[$i] = "$($ver_a[$i])"
|
||||
function Get-InstalledVersion {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Get all installed version of app, by checking version directories' 'install.json'
|
||||
.PARAMETER AppName
|
||||
App's name
|
||||
.PARAMETER Global
|
||||
Globally installed application
|
||||
.NOTES
|
||||
Versions are sorted from oldest to newest, i.e., latest installed version is the last one in the output array.
|
||||
If no installed version found, empty array will be returned.
|
||||
#>
|
||||
[OutputType([Object[]])]
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
|
||||
[Alias('App')]
|
||||
[String]
|
||||
$AppName,
|
||||
[Parameter(Position = 1)]
|
||||
[Switch]
|
||||
$Global
|
||||
)
|
||||
process {
|
||||
$appPath = appdir $AppName $Global
|
||||
if (Test-Path $appPath) {
|
||||
$versions = @((Get-ChildItem "$appPath\*\install.json" | Sort-Object -Property LastWriteTimeUtc).Directory.Name)
|
||||
return $versions | Where-Object { ($_ -ne 'current') -and ($_ -notlike '_*.old*') }
|
||||
} else {
|
||||
return @()
|
||||
}
|
||||
}
|
||||
# Deprecated
|
||||
# sort_versions (Get-ChildItem $appPath -dir -attr !reparsePoint | Where-Object { $null -ne $(Get-ChildItem $_.FullName) } | ForEach-Object { $_.Name })
|
||||
}
|
||||
|
||||
function Compare-Version {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Compare versions, mainly according to SemVer's rules
|
||||
.PARAMETER ReferenceVersion
|
||||
Specifies a version used as a reference for comparison
|
||||
.PARAMETER DifferenceVersion
|
||||
Specifies the version that are compared to the reference version
|
||||
.PARAMETER Delimiter
|
||||
Specifies the delimiter of versions
|
||||
.OUTPUTS
|
||||
System.Int32
|
||||
'0' if DifferenceVersion is equal to ReferenceVersion,
|
||||
'1' if DifferenceVersion is greater then ReferenceVersion,
|
||||
'-1' if DifferenceVersion is less then ReferenceVersion
|
||||
#>
|
||||
[OutputType([Int32])]
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[Parameter(Mandatory = $true, Position = 0)]
|
||||
[AllowEmptyString()]
|
||||
[String]
|
||||
$ReferenceVersion,
|
||||
[Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $true)]
|
||||
[AllowEmptyString()]
|
||||
[String]
|
||||
$DifferenceVersion,
|
||||
[String]
|
||||
$Delimiter = '-'
|
||||
)
|
||||
process {
|
||||
# Use '+' sign as post-release, see https://github.com/ScoopInstaller/Scoop/pull/3721#issuecomment-553718093
|
||||
$ReferenceVersion, $DifferenceVersion = @($ReferenceVersion, $DifferenceVersion) -replace '\+', '-'
|
||||
|
||||
# Return 0 if versions are equal
|
||||
if ($DifferenceVersion -eq $ReferenceVersion) {
|
||||
return 0
|
||||
}
|
||||
|
||||
if($ver_a[$i] -gt $ver_b[$i]) { return 1; }
|
||||
if($ver_a[$i] -lt $ver_b[$i]) { return -1; }
|
||||
# Preprocess versions (split, convert and separate)
|
||||
$splitReferenceVersion = @(SplitVersion -Version $ReferenceVersion -Delimiter $Delimiter)
|
||||
$splitDifferenceVersion = @(SplitVersion -Version $DifferenceVersion -Delimiter $Delimiter)
|
||||
|
||||
# Nightly versions are always equal
|
||||
if ($splitReferenceVersion[0] -eq 'nightly' -and $splitDifferenceVersion[0] -eq 'nightly') {
|
||||
return 0
|
||||
}
|
||||
|
||||
for ($i = 0; $i -lt [Math]::Max($splitReferenceVersion.Length, $splitDifferenceVersion.Length); $i++) {
|
||||
# '1.1-alpha' is less then '1.1'
|
||||
if ($i -ge $splitReferenceVersion.Length) {
|
||||
if ($splitDifferenceVersion[$i] -match 'alpha|beta|rc|pre') {
|
||||
return -1
|
||||
} else {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
# '1.1' is greater then '1.1-beta'
|
||||
if ($i -ge $splitDifferenceVersion.Length) {
|
||||
if ($splitReferenceVersion[$i] -match 'alpha|beta|rc|pre') {
|
||||
return 1
|
||||
} else {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
# If some parts of versions have '.', compare them with delimiter '.'
|
||||
if (($splitReferenceVersion[$i] -match '\.') -or ($splitDifferenceVersion[$i] -match '\.')) {
|
||||
$Result = Compare-Version -ReferenceVersion $splitReferenceVersion[$i] -DifferenceVersion $splitDifferenceVersion[$i] -Delimiter '.'
|
||||
# If the parts are equal, continue to next part, otherwise return
|
||||
if ($Result -ne 0) {
|
||||
return $Result
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
# If some parts of versions have '_', compare them with delimiter '_'
|
||||
if (($splitReferenceVersion[$i] -match '_') -or ($splitDifferenceVersion[$i] -match '_')) {
|
||||
$Result = Compare-Version -ReferenceVersion $splitReferenceVersion[$i] -DifferenceVersion $splitDifferenceVersion[$i] -Delimiter '_'
|
||||
# If the parts are equal, continue to next part, otherwise return
|
||||
if ($Result -ne 0) {
|
||||
return $Result
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
# Don't try to compare [Long] to [String]
|
||||
if ($null -ne $splitReferenceVersion[$i] -and $null -ne $splitDifferenceVersion[$i]) {
|
||||
if ($splitReferenceVersion[$i] -is [String] -and $splitDifferenceVersion[$i] -isnot [String]) {
|
||||
$splitDifferenceVersion[$i] = "$($splitDifferenceVersion[$i])"
|
||||
}
|
||||
if ($splitDifferenceVersion[$i] -is [String] -and $splitReferenceVersion[$i] -isnot [String]) {
|
||||
$splitReferenceVersion[$i] = "$($splitReferenceVersion[$i])"
|
||||
}
|
||||
}
|
||||
|
||||
# Compare [String] or [Long]
|
||||
if ($splitDifferenceVersion[$i] -gt $splitReferenceVersion[$i]) {
|
||||
return 1
|
||||
}
|
||||
if ($splitDifferenceVersion[$i] -lt $splitReferenceVersion[$i]) {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
}
|
||||
if($ver_b.length -gt $ver_a.length) { return -1 }
|
||||
return 0
|
||||
}
|
||||
|
||||
# Helper function
|
||||
function SplitVersion {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Split version by Delimiter, convert number string to number, and separate letters from numbers
|
||||
.PARAMETER Version
|
||||
Specifies a version
|
||||
.PARAMETER Delimiter
|
||||
Specifies the delimiter of version (Literal)
|
||||
#>
|
||||
[OutputType([Object[]])]
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
|
||||
[AllowEmptyString()]
|
||||
[String]
|
||||
$Version,
|
||||
[String]
|
||||
$Delimiter = '-'
|
||||
)
|
||||
process {
|
||||
$Version = $Version -replace '[a-zA-Z]+', "$Delimiter$&$Delimiter"
|
||||
return ($Version -split [Regex]::Escape($Delimiter) -ne '' | ForEach-Object { if ($_ -match '^\d+$') { [Long]$_ } else { $_ } })
|
||||
}
|
||||
}
|
||||
|
||||
# Deprecated
|
||||
# Not used anymore in scoop core
|
||||
function qsort($ary, $fn) {
|
||||
if($null -eq $ary) { return @() }
|
||||
if(!($ary -is [array])) { return @($ary) }
|
||||
warn '"qsort" is deprecated. Please avoid using it anymore.'
|
||||
if ($null -eq $ary) { return @() }
|
||||
if (!($ary -is [array])) { return @($ary) }
|
||||
|
||||
$pivot = $ary[0]
|
||||
$rem = $ary[1..($ary.length-1)]
|
||||
$rem = $ary[1..($ary.length - 1)]
|
||||
|
||||
$lesser = qsort ($rem | Where-Object { (& $fn $_ $pivot) -lt 0 }) $fn
|
||||
$lesser = qsort ($rem | Where-Object { (& $fn $pivot $_) -lt 0 }) $fn
|
||||
|
||||
$greater = qsort ($rem | Where-Object { (& $fn $_ $pivot) -ge 0 }) $fn
|
||||
$greater = qsort ($rem | Where-Object { (& $fn $pivot $_) -ge 0 }) $fn
|
||||
|
||||
return @() + $lesser + @($pivot) + $greater
|
||||
}
|
||||
function sort_versions($versions) { qsort $versions compare_versions }
|
||||
|
||||
# Deprecated
|
||||
# Not used anymore in scoop core
|
||||
function sort_versions($versions) {
|
||||
warn '"sort_versions" is deprecated. Please avoid using it anymore.'
|
||||
qsort $versions Compare-Version
|
||||
}
|
||||
|
||||
function compare_versions($a, $b) {
|
||||
Show-DeprecatedWarning $MyInvocation 'Compare-Version'
|
||||
# Please note the parameters' sequence
|
||||
return Compare-Version -ReferenceVersion $b -DifferenceVersion $a
|
||||
}
|
||||
|
||||
function latest_version($app, $bucket, $url) {
|
||||
Show-DeprecatedWarning $MyInvocation 'Get-LatestVersion'
|
||||
return Get-LatestVersion -AppName $app -Bucket $bucket -Uri $url
|
||||
}
|
||||
|
||||
function current_version($app, $global) {
|
||||
Show-DeprecatedWarning $MyInvocation 'Select-CurrentVersion'
|
||||
return Select-CurrentVersion -AppName $app -Global:$global
|
||||
}
|
||||
|
||||
function versions($app, $global) {
|
||||
Show-DeprecatedWarning $MyInvocation 'Get-InstalledVersion'
|
||||
return Get-InstalledVersion -AppName $app -Global:$global
|
||||
}
|
||||
|
||||
@@ -16,36 +16,34 @@
|
||||
# -v, --verbose Show alias description and table headers (works only for 'list')
|
||||
|
||||
param(
|
||||
[String]$opt,
|
||||
[String]$name,
|
||||
[String]$command,
|
||||
[String]$description,
|
||||
[Switch]$verbose = $false
|
||||
[String]$opt,
|
||||
[String]$name,
|
||||
[String]$command,
|
||||
[String]$description,
|
||||
[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"
|
||||
$script:config_alias = 'alias'
|
||||
|
||||
function init_alias_config {
|
||||
$aliases = get_config $script:config_alias
|
||||
if(!$aliases) {
|
||||
$aliases = @{}
|
||||
if ($aliases) {
|
||||
$aliases
|
||||
} else {
|
||||
New-Object -TypeName PSObject
|
||||
}
|
||||
|
||||
return $aliases
|
||||
}
|
||||
|
||||
function add_alias($name, $command) {
|
||||
if(!$command) {
|
||||
if (!$command) {
|
||||
abort "Can't create an empty alias."
|
||||
}
|
||||
|
||||
# get current aliases from config
|
||||
$aliases = init_alias_config
|
||||
if($aliases.$name) {
|
||||
if ($aliases.$name) {
|
||||
abort "Alias $name already exists."
|
||||
}
|
||||
|
||||
@@ -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 utf8
|
||||
@(
|
||||
"# 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
|
||||
@@ -68,11 +66,11 @@ $command
|
||||
|
||||
function rm_alias($name) {
|
||||
$aliases = init_alias_config
|
||||
if(!$name) {
|
||||
abort "Which alias should be removed?"
|
||||
if (!$name) {
|
||||
abort 'Which alias should be removed?'
|
||||
}
|
||||
|
||||
if($aliases.$name) {
|
||||
if ($aliases.$name) {
|
||||
"Removing alias $name..."
|
||||
|
||||
rm_shim $aliases.$name (shimdir $false)
|
||||
@@ -92,24 +90,24 @@ function list_aliases {
|
||||
$command = ($content | Select-Object -Skip 1).Trim()
|
||||
$summary = (summary $content).Trim()
|
||||
|
||||
$aliases += New-Object psobject -Property @{Name=$_.name; Summary=$summary; Command=$command}
|
||||
$aliases += New-Object psobject -Property @{Name = $_.name; Summary = $summary; Command = $command }
|
||||
}
|
||||
|
||||
if(!$aliases.count) {
|
||||
warn "No aliases founds."
|
||||
if (!$aliases.count) {
|
||||
info "No alias found."
|
||||
}
|
||||
$aliases = $aliases.GetEnumerator() | Sort-Object Name
|
||||
if($verbose) {
|
||||
return $aliases | Select-Object Name, Command, Summary | Format-Table -autosize -wrap
|
||||
if ($verbose) {
|
||||
return $aliases | Select-Object Name, Command, Summary
|
||||
} else {
|
||||
return $aliases | Select-Object Name, Command | Format-Table -autosize -hidetablehead -wrap
|
||||
return $aliases | Select-Object Name, Command
|
||||
}
|
||||
}
|
||||
|
||||
switch($opt) {
|
||||
"add" { add_alias $name $command }
|
||||
"rm" { rm_alias $name }
|
||||
"list" { list_aliases }
|
||||
switch ($opt) {
|
||||
'add' { add_alias $name $command }
|
||||
'rm' { rm_alias $name }
|
||||
'list' { list_aliases }
|
||||
default { my_usage; exit 1 }
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
# scoop bucket add <name> [<repo>]
|
||||
#
|
||||
# e.g.:
|
||||
# scoop bucket add extras https://github.com/lukesampson/scoop-extras.git
|
||||
# scoop bucket add extras https://github.com/ScoopInstaller/Extras.git
|
||||
#
|
||||
# Since the 'extras' bucket is known to Scoop, this can be shortened to:
|
||||
# scoop bucket add extras
|
||||
@@ -19,22 +19,53 @@
|
||||
# scoop bucket known
|
||||
param($cmd, $name, $repo)
|
||||
|
||||
. "$psscriptroot\..\lib\core.ps1"
|
||||
. "$psscriptroot\..\lib\buckets.ps1"
|
||||
. "$psscriptroot\..\lib\help.ps1"
|
||||
. "$psscriptroot\..\lib\git.ps1"
|
||||
$usage_add = 'usage: scoop bucket add <name> [<repo>]'
|
||||
$usage_rm = 'usage: scoop bucket rm <name>'
|
||||
|
||||
reset_aliases
|
||||
|
||||
$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' { Get-LocalBucket }
|
||||
'known' { known_buckets }
|
||||
default { "scoop bucket: cmd '$cmd' not supported"; my_usage; exit 1 }
|
||||
switch ($cmd) {
|
||||
'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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Usage: scoop cache show|rm [app]
|
||||
# Usage: scoop cache show|rm [app(s)]
|
||||
# Summary: Show or clear the download cache
|
||||
# Help: Scoop caches downloads so you don't need to download the same files
|
||||
# when you uninstall and re-install the same version of an app.
|
||||
@@ -10,48 +10,63 @@
|
||||
#
|
||||
# To clear everything in your cache, use:
|
||||
# scoop cache rm *
|
||||
param($cmd, $app)
|
||||
# You can also use the `-a/--all` switch in place of `*` here
|
||||
|
||||
. "$psscriptroot\..\lib\help.ps1"
|
||||
|
||||
reset_aliases
|
||||
param($cmd)
|
||||
|
||||
function cacheinfo($file) {
|
||||
$app, $version, $url = $file.name -split '#'
|
||||
$size = filesize $file.length
|
||||
return new-object psobject -prop @{ app=$app; version=$version; url=$url; size=$size }
|
||||
$app, $version, $url = $file.Name -split '#'
|
||||
New-Object PSObject -Property @{ Name = $app; Version = $version; Length = $file.Length; URL = $url }
|
||||
}
|
||||
|
||||
function show($app) {
|
||||
$files = @(Get-ChildItem "$cachedir" | Where-Object { $_.name -match "^$app" })
|
||||
$total_length = ($files | Measure-Object length -sum).sum -as [double]
|
||||
function cacheshow($app) {
|
||||
if (!$app -or $app -eq '*') {
|
||||
$app = '.*?'
|
||||
} else {
|
||||
$app = '(' + ($app -join '|') + ')'
|
||||
}
|
||||
$files = @(Get-ChildItem $cachedir | Where-Object -Property Name -Value "^$app#" -Match)
|
||||
$totalLength = ($files | Measure-Object -Property Length -Sum).Sum
|
||||
|
||||
$f_app = @{ expression={"$($_.app) ($($_.version))" }}
|
||||
$f_url = @{ expression={$_.url};alignment='right'}
|
||||
$f_size = @{ expression={$_.size}; alignment='right'}
|
||||
$files | ForEach-Object { cacheinfo $_ } | Select-Object Name, Version, Length, URL
|
||||
|
||||
Write-Host "Total: $($files.Length) $(pluralize $files.Length 'file' 'files'), $(filesize $totalLength)" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
$files | ForEach-Object { cacheinfo $_ } | Format-Table $f_size, $f_app, $f_url -auto -hide
|
||||
function cacheremove($app) {
|
||||
if (!$app) {
|
||||
'ERROR: <app(s)> missing'
|
||||
my_usage
|
||||
exit 1
|
||||
} elseif ($app -eq '*' -or $app -eq '-a' -or $app -eq '--all') {
|
||||
$files = @(Get-ChildItem $cachedir)
|
||||
} else {
|
||||
$app = '(' + ($app -join '|') + ')'
|
||||
$files = @(Get-ChildItem $cachedir | Where-Object -Property Name -Value "^$app#" -Match)
|
||||
}
|
||||
$totalLength = ($files | Measure-Object -Property Length -Sum).Sum
|
||||
|
||||
"Total: $($files.length) $(pluralize $files.length 'file' 'files'), $(filesize $total_length)"
|
||||
$files | ForEach-Object {
|
||||
$curr = cacheinfo $_
|
||||
Write-Host "Removing $($curr.URL)..."
|
||||
Remove-Item $_.FullName
|
||||
if(Test-Path "$cachedir\$($curr.Name).txt") {
|
||||
Remove-Item "$cachedir\$($curr.Name).txt"
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "Deleted: $($files.Length) $(pluralize $files.Length 'file' 'files'), $(filesize $totalLength)" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
switch($cmd) {
|
||||
'rm' {
|
||||
if(!$app) { 'ERROR: <app> missing'; my_usage; exit 1 }
|
||||
Remove-Item "$cachedir\$app#*"
|
||||
if(test-path("$cachedir\$app.txt")) {
|
||||
Remove-Item "$cachedir\$app.txt"
|
||||
}
|
||||
cacheremove $Args
|
||||
}
|
||||
'show' {
|
||||
show $app
|
||||
}
|
||||
'' {
|
||||
show
|
||||
cacheshow $Args
|
||||
}
|
||||
default {
|
||||
my_usage
|
||||
cacheshow (@($cmd) + $Args)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
24
libexec/scoop-cat.ps1
Normal file
24
libexec/scoop-cat.ps1
Normal file
@@ -0,0 +1,24 @@
|
||||
# Usage: scoop cat <app>
|
||||
# Summary: Show content of specified manifest. If available, `bat` will be used to pretty-print the JSON.
|
||||
|
||||
param($app)
|
||||
|
||||
. "$PSScriptRoot\..\lib\json.ps1" # 'ConvertToPrettyJson'
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Get-Manifest'
|
||||
|
||||
if (!$app) { error '<app> missing'; my_usage; exit 1 }
|
||||
|
||||
$null, $manifest, $bucket, $url = Get-Manifest $app
|
||||
|
||||
if ($manifest) {
|
||||
$style = get_config cat_style
|
||||
if ($style) {
|
||||
$manifest | ConvertToPrettyJson | bat --no-paging --style $style --language json
|
||||
} else {
|
||||
$manifest | ConvertToPrettyJson
|
||||
}
|
||||
} else {
|
||||
abort "Couldn't find manifest for '$app'$(if($url) { " at the URL $url" })."
|
||||
}
|
||||
|
||||
exit $exitCode
|
||||
@@ -3,13 +3,18 @@
|
||||
# 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"
|
||||
. "$PSScriptRoot\..\lib\diagnostic.ps1"
|
||||
|
||||
$issues = 0
|
||||
$defenderIssues = 0
|
||||
|
||||
$adminPrivileges = ([System.Security.Principal.WindowsPrincipal] [System.Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)
|
||||
|
||||
if ($adminPrivileges) {
|
||||
$defenderIssues += !(check_windows_defender $false)
|
||||
$defenderIssues += !(check_windows_defender $true)
|
||||
}
|
||||
|
||||
$issues += !(check_windows_defender $false)
|
||||
$issues += !(check_windows_defender $true)
|
||||
$issues += !(check_main_bucket)
|
||||
$issues += !(check_long_paths)
|
||||
|
||||
@@ -29,19 +34,22 @@ if (!(Test-HelperInstalled -Helper Dark)) {
|
||||
}
|
||||
|
||||
$globaldir = New-Object System.IO.DriveInfo($globaldir)
|
||||
if($globaldir.DriveFormat -ne 'NTFS') {
|
||||
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."
|
||||
$issues++
|
||||
}
|
||||
|
||||
$scoopdir = New-Object System.IO.DriveInfo($scoopdir)
|
||||
if($scoopdir.DriveFormat -ne 'NTFS') {
|
||||
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."
|
||||
$issues++
|
||||
}
|
||||
|
||||
if($issues) {
|
||||
if ($issues) {
|
||||
warn "Found $issues potential $(pluralize $issues problem problems)."
|
||||
} elseif ($defenderIssues) {
|
||||
info "Found $defenderIssues performance $(pluralize $defenderIssues problem problems)."
|
||||
warn "Security is more important than performance, in most cases."
|
||||
} else {
|
||||
success "No problems identified!"
|
||||
}
|
||||
|
||||
@@ -3,70 +3,78 @@
|
||||
# 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\getopt.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
|
||||
}
|
||||
|
||||
function cleanup($app, $global, $verbose, $cache) {
|
||||
$current_version = current_version $app $global
|
||||
$current_version = Select-CurrentVersion -AppName $app -Global:$global
|
||||
if ($cache) {
|
||||
Remove-Item "$cachedir\$app#*" -Exclude "$app#$current_version#*"
|
||||
}
|
||||
$versions = versions $app $global | Where-Object { $_ -ne $current_version -and $_ -ne 'current' }
|
||||
$appDir = appdir $app $global
|
||||
$versions = Get-ChildItem $appDir -Name
|
||||
$versions = $versions | Where-Object { $current_version -ne $_ -and $_ -ne 'current' }
|
||||
if (!$versions) {
|
||||
if ($verbose) { success "$app is already clean" }
|
||||
return
|
||||
}
|
||||
|
||||
write-host -f yellow "Removing $app`:" -nonewline
|
||||
Write-Host -f yellow "Removing $app`:" -NoNewline
|
||||
$versions | ForEach-Object {
|
||||
$version = $_
|
||||
write-host " $version" -nonewline
|
||||
Write-Host " $version" -NoNewline
|
||||
$dir = versiondir $app $version $global
|
||||
# unlink all potential old link before doing recursive Remove-Item
|
||||
unlink_persist_data $dir
|
||||
unlink_persist_data (installed_manifest $app $version $global) $dir
|
||||
Remove-Item $dir -ErrorAction Stop -Recurse -Force
|
||||
}
|
||||
write-host ''
|
||||
$leftVersions = Get-ChildItem $appDir
|
||||
if ($leftVersions.Length -eq 1 -and $leftVersions.Name -eq 'current' -and $leftVersions.LinkType) {
|
||||
attrib $leftVersions.FullName -R /L
|
||||
Remove-Item $leftVersions.FullName -ErrorAction Stop -Force
|
||||
$leftVersions = $null
|
||||
}
|
||||
if (!$leftVersions) {
|
||||
Remove-Item $appDir -ErrorAction Stop -Force
|
||||
}
|
||||
Write-Host ''
|
||||
}
|
||||
|
||||
if ($apps) {
|
||||
$verbose = $true
|
||||
if ($apps -eq '*') {
|
||||
if ($apps -or $all) {
|
||||
if ($apps -eq '*' -or $all) {
|
||||
$verbose = $false
|
||||
$apps = applist (installed_apps $false) $false
|
||||
if ($global) {
|
||||
$apps += applist (installed_apps $true) $true
|
||||
}
|
||||
} else {
|
||||
$verbose = $true
|
||||
$apps = Confirm-InstallationStatus $apps -Global:$global
|
||||
}
|
||||
|
||||
# $apps is now a list of ($app, $global) tuples
|
||||
$apps | ForEach-Object { cleanup @_ $verbose $cache}
|
||||
$apps | ForEach-Object { cleanup @_ $verbose $cache }
|
||||
|
||||
if ($cache) {
|
||||
Remove-Item "$cachedir\*.download" -ErrorAction Ignore
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
# Summary: Get or set configuration values
|
||||
# Help: The scoop configuration file is saved at ~/.config/scoop/config.json.
|
||||
#
|
||||
# To get all configuration settings:
|
||||
#
|
||||
# scoop config
|
||||
#
|
||||
# To get a configuration setting:
|
||||
#
|
||||
# scoop config <name>
|
||||
@@ -17,36 +21,133 @@
|
||||
# Settings
|
||||
# --------
|
||||
#
|
||||
# 7ZIPEXTRACT_USE_EXTERNAL: $true|$false
|
||||
# External 7zip (from path) will be used for archives extraction.
|
||||
#
|
||||
# MSIEXTRACT_USE_LESSMSI: $true|$false
|
||||
# Prefer lessmsi utility over native msiexec.
|
||||
#
|
||||
# NO_JUNCTIONS: $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
|
||||
# Git repository containining scoop source code.
|
||||
# This configuration is useful for custom forks.
|
||||
#
|
||||
# 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')
|
||||
#
|
||||
# proxy: [username:password@]host:port
|
||||
# By default, Scoop will use the proxy settings from Internet Options, but with anonymous authentication.
|
||||
#
|
||||
# By default, Scoop will use the proxy settings from Internet Options, but with anonymous authentication.
|
||||
# * To use the credentials for the current logged-in user, use 'currentuser' in place of username:password
|
||||
# * To use the system proxy settings configured in Internet Options, use 'default' in place of host:port
|
||||
# * 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)
|
||||
#
|
||||
# * To use the credentials for the current logged-in user, use 'currentuser' in place of username:password
|
||||
# * To use the system proxy settings configured in Internet Options, use 'default' in place of host:port
|
||||
# * 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
|
||||
# Allow to configure preferred architecture for application installation.
|
||||
# If not specified, architecture is determined be system.
|
||||
#
|
||||
# debug: $true|$false
|
||||
# Additional and detailed output will be shown.
|
||||
#
|
||||
# force_update: $true|$false
|
||||
# Force apps updating to bucket's version.
|
||||
#
|
||||
# show_update_log: $true|$false
|
||||
# Do not show changed commits on 'scoop update'
|
||||
#
|
||||
# manifest_review: $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
|
||||
# Path to Scoop root directory.
|
||||
#
|
||||
# globalPath: $Env:ProgramData\scoop
|
||||
# Path to Scoop root directory for global apps.
|
||||
#
|
||||
# cachePath:
|
||||
# For downloads, defaults to 'cache' folder under Scoop root directory.
|
||||
#
|
||||
# gh_token:
|
||||
# GitHub API token used to make authenticated requests.
|
||||
# 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.
|
||||
# See: 'https://support.virustotal.com/hc/en-us/articles/115002088769-Please-give-me-an-API-key'
|
||||
#
|
||||
# cat_style:
|
||||
# When set to a non-empty string, Scoop will use 'bat' to display the manifest for
|
||||
# the `scoop cat` command and while doing manifest review. This requires 'bat' to be
|
||||
# installed (run `scoop install bat` to install it), otherwise errors will be thrown.
|
||||
# The accepted values are the same as ones passed to the --style flag of 'bat'.
|
||||
#
|
||||
# ignore_running_processes: $true|$false
|
||||
# When set to $false (default), Scoop would stop its procedure immediately if it detects
|
||||
# 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.
|
||||
#
|
||||
# ARIA2 configuration
|
||||
# -------------------
|
||||
#
|
||||
# aria2-enabled: $true|$false
|
||||
# Aria2c will be used for downloading of artifacts.
|
||||
#
|
||||
# aria2-warning-enabled: $true|$false
|
||||
# Disable Aria2c warning which is shown while downloading.
|
||||
#
|
||||
# aria2-retry-wait: 2
|
||||
# Number of seconds to wait between retries.
|
||||
# See: 'https://aria2.github.io/manual/en/html/aria2c.html#cmdoption-retry-wait'
|
||||
#
|
||||
# aria2-split: 5
|
||||
# Number of connections used for downlaod.
|
||||
# See: 'https://aria2.github.io/manual/en/html/aria2c.html#cmdoption-s'
|
||||
#
|
||||
# aria2-max-connection-per-server: 5
|
||||
# The maximum number of connections to one server for each download.
|
||||
# See: 'https://aria2.github.io/manual/en/html/aria2c.html#cmdoption-x'
|
||||
#
|
||||
# aria2-min-split-size: 5M
|
||||
# Downloaded files will be splitted by this configured size and downloaded using multiple connections.
|
||||
# See: 'https://aria2.github.io/manual/en/html/aria2c.html#cmdoption-k'
|
||||
#
|
||||
# aria2-options:
|
||||
# Array of additional aria2 options.
|
||||
# See: 'https://aria2.github.io/manual/en/html/aria2c.html#options'
|
||||
|
||||
param($name, $value)
|
||||
|
||||
. "$psscriptroot\..\lib\core.ps1"
|
||||
. "$psscriptroot\..\lib\help.ps1"
|
||||
|
||||
reset_aliases
|
||||
|
||||
if(!$name) { my_usage; exit 1 }
|
||||
|
||||
if($name -like 'rm') {
|
||||
if (!$name) {
|
||||
$scoopConfig
|
||||
} elseif ($name -like '--help') {
|
||||
my_usage
|
||||
} elseif ($name -like 'rm') {
|
||||
set_config $value $null | Out-Null
|
||||
Write-Output "'$value' has been removed"
|
||||
} elseif($null -ne $value) {
|
||||
Write-Host "'$value' has been removed"
|
||||
} elseif ($null -ne $value) {
|
||||
set_config $name $value | Out-Null
|
||||
Write-Output "'$name' has been set to '$value'"
|
||||
Write-Host "'$name' has been set to '$value'"
|
||||
} else {
|
||||
$value = get_config $name
|
||||
if($null -eq $value) {
|
||||
Write-Output "'$name' is not set"
|
||||
Write-Host "'$name' is not set"
|
||||
} else {
|
||||
Write-Output $value
|
||||
$value
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,29 +11,28 @@ function create_manifest($url) {
|
||||
$url_parts = $null
|
||||
try {
|
||||
$url_parts = parse_url $url
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
abort "Error: $url is not a valid URL"
|
||||
}
|
||||
|
||||
$name = choose_item $url_parts "App name"
|
||||
$name = choose_item $url_parts 'App name'
|
||||
$name = if ($name.Length -gt 0) {
|
||||
$name
|
||||
}
|
||||
else {
|
||||
file_name ($url_parts | select-object -last 1)
|
||||
} else {
|
||||
file_name ($url_parts | Select-Object -Last 1)
|
||||
}
|
||||
|
||||
$manifest.version = choose_item $url_parts "Version"
|
||||
$manifest.version = choose_item $url_parts 'Version'
|
||||
|
||||
$manifest | convertto-json | out-file -filepath "$name.json" -encoding utf8
|
||||
$manifest_path = join-path $pwd "$name.json"
|
||||
write-host "Created '$manifest_path'."
|
||||
$manifest | ConvertTo-Json | Out-File -FilePath "$name.json" -Encoding ASCII
|
||||
$manifest_path = Join-Path $pwd "$name.json"
|
||||
Write-Host "Created '$manifest_path'."
|
||||
}
|
||||
|
||||
function new_manifest() {
|
||||
@{ "homepage" = ""; "license" = ""; "version" = ""; "url" = "";
|
||||
"hash" = ""; "extract_dir" = ""; "bin" = ""; "depends" = "" }
|
||||
@{ 'homepage' = ''; 'license' = ''; 'version' = ''; 'url' = '';
|
||||
'hash' = ''; 'extract_dir' = ''; 'bin' = ''; 'depends' = ''
|
||||
}
|
||||
}
|
||||
|
||||
function file_name($segment) {
|
||||
@@ -41,19 +40,19 @@ function file_name($segment) {
|
||||
}
|
||||
|
||||
function parse_url($url) {
|
||||
$uri = new-object Uri $url
|
||||
$uri.pathandquery.substring(1).split("/")
|
||||
$uri = New-Object Uri $url
|
||||
$uri.pathandquery.substring(1).split('/')
|
||||
}
|
||||
|
||||
function choose_item($list, $query) {
|
||||
for ($i = 0; $i -lt $list.count; $i++) {
|
||||
$item = $list[$i]
|
||||
write-host "$($i + 1)) $item"
|
||||
Write-Host "$($i + 1)) $item"
|
||||
}
|
||||
$sel = read-host $query
|
||||
$sel = Read-Host $query
|
||||
|
||||
if ($sel.trim() -match '^[0-9+]$') {
|
||||
return $list[$sel-1]
|
||||
return $list[$sel - 1]
|
||||
}
|
||||
|
||||
$sel
|
||||
@@ -61,8 +60,7 @@ function choose_item($list, $query) {
|
||||
|
||||
if (!$url) {
|
||||
scoop help create
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
create_manifest $url
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
# 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\getopt.ps1"
|
||||
. "$PSScriptRoot\..\lib\depends.ps1" # 'Get-Dependency'
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1" # 'default_architecture'
|
||||
|
||||
$opt, $apps, $err = getopt $args 'a:' 'arch='
|
||||
$app = $apps[0]
|
||||
@@ -23,9 +17,12 @@ try {
|
||||
abort "ERROR: $_"
|
||||
}
|
||||
|
||||
$deps = @(deps $app $architecture)
|
||||
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
|
||||
|
||||
138
libexec/scoop-download.ps1
Normal file
138
libexec/scoop-download.ps1
Normal file
@@ -0,0 +1,138 @@
|
||||
# Usage: scoop download <app> [options]
|
||||
# Summary: Download apps in the cache folder and verify hashes
|
||||
# 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
|
||||
#
|
||||
# To download an app from a manifest on your computer
|
||||
# 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
|
||||
|
||||
. "$PSScriptRoot\..\lib\getopt.ps1"
|
||||
. "$PSScriptRoot\..\lib\json.ps1" # 'autoupdate.ps1' (indirectly)
|
||||
. "$PSScriptRoot\..\lib\autoupdate.ps1" # 'generate_user_manifest' (indirectly)
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1" # 'default_architecture' 'generate_user_manifest' 'Get-Manifest'
|
||||
. "$PSScriptRoot\..\lib\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
|
||||
try {
|
||||
$architecture = ensure_architecture ($opt.a + $opt.arch)
|
||||
} catch {
|
||||
abort "ERROR: $_"
|
||||
}
|
||||
|
||||
if (!$apps) { error '<app> missing'; my_usage; exit 1 }
|
||||
|
||||
if (is_scoop_outdated) {
|
||||
if ($opt.u -or $opt.'no-update-scoop') {
|
||||
warn "Scoop is out of date."
|
||||
} else {
|
||||
scoop update
|
||||
}
|
||||
}
|
||||
|
||||
# we only want to show this warning once
|
||||
if(!$use_cache) { warn "Cache is being ignored." }
|
||||
|
||||
foreach ($curr_app in $apps) {
|
||||
# Prevent leaking variables from previous iteration
|
||||
$bucket = $version = $app = $manifest = $url = $null
|
||||
|
||||
$app, $bucket, $version = parse_app $curr_app
|
||||
$app, $manifest, $bucket, $url = Get-Manifest "$bucket/$app"
|
||||
|
||||
info "Starting download for $app..."
|
||||
|
||||
# Generate manifest if there is different version in manifest
|
||||
if (($null -ne $version) -and ($manifest.version -ne $version)) {
|
||||
$generated = generate_user_manifest $app $bucket $version
|
||||
if ($null -eq $generated) {
|
||||
error 'Manifest cannot be generated with provided version'
|
||||
continue
|
||||
}
|
||||
$manifest = parse_json($generated)
|
||||
}
|
||||
|
||||
if(!$manifest) {
|
||||
error "Couldn't find manifest for '$app'$(if($url) { " at the URL $url" })."
|
||||
continue
|
||||
}
|
||||
$version = $manifest.version
|
||||
if(!$version) {
|
||||
error "Manifest doesn't specify a version."
|
||||
continue
|
||||
}
|
||||
if($version -match '[^\w\.\-\+_]') {
|
||||
error "Manifest version has unsupported character '$($matches[0])'."
|
||||
continue
|
||||
}
|
||||
|
||||
$curr_check_hash = $check_hash
|
||||
if ($version -eq 'nightly') {
|
||||
$version = nightly_version $(get-date)
|
||||
$curr_check_hash = $false
|
||||
}
|
||||
|
||||
if(!(supports_architecture $manifest $architecture)) {
|
||||
error "'$app' doesn't support $architecture architecture!"
|
||||
continue
|
||||
}
|
||||
|
||||
if(Test-Aria2Enabled) {
|
||||
dl_with_cache_aria2 $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
|
||||
} catch {
|
||||
write-host -f darkred $_
|
||||
error "URL $url is not valid"
|
||||
$dl_failure = $true
|
||||
continue
|
||||
}
|
||||
|
||||
if($curr_check_hash) {
|
||||
$manifest_hash = hash_for_url $manifest $url $architecture
|
||||
$cached = cache_path $app $version $url
|
||||
$ok, $err = check_hash $cached $manifest_hash (show_app $app $bucket)
|
||||
|
||||
if(!$ok) {
|
||||
error $err
|
||||
if(test-path $cached) {
|
||||
# rm cached file
|
||||
Remove-Item -force $cached
|
||||
}
|
||||
if ($url -like '*sourceforge.net*') {
|
||||
warn 'SourceForge.net is known for causing hash validation fails. Please try again before opening a ticket.'
|
||||
}
|
||||
error (new_issue_msg $app $bucket "hash check failed")
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
info "Skipping hash verification."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 = current_version $app $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 'lastUpdate', 'rootPath', 'globalPath', 'cachePath', '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,33 +1,58 @@
|
||||
# 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\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 (!(installed $app $global)) {
|
||||
if ($global) {
|
||||
error "'$app' is not installed globally."
|
||||
} else {
|
||||
error "'$app' is not installed."
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
$dir = versiondir $app 'current' $global
|
||||
$json = install_info $app 'current' $global
|
||||
if (get_config NO_JUNCTIONS) {
|
||||
$version = Select-CurrentVersion -App $app -Global:$global
|
||||
} else {
|
||||
$version = 'current'
|
||||
}
|
||||
$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 locked and can not be updated anymore."
|
||||
success "$app is now held and can not be updated anymore."
|
||||
}
|
||||
|
||||
exit $exitcode
|
||||
|
||||
@@ -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
|
||||
|
||||
64
libexec/scoop-import.ps1
Normal file
64
libexec/scoop-import.ps1
Normal file
@@ -0,0 +1,64 @@
|
||||
# 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 = default_architecture
|
||||
|
||||
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 '32bit' -eq $def_arch) {
|
||||
' --arch 64bit'
|
||||
} elseif ('32bit' -in $info -and '64bit' -eq $def_arch) {
|
||||
' --arch 32bit'
|
||||
} 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
|
||||
}
|
||||
}
|
||||
@@ -1,139 +1,245 @@
|
||||
# Usage: scoop info <app>
|
||||
# Usage: scoop info <app> [--verbose]
|
||||
# Summary: Display information about an app
|
||||
param($app)
|
||||
# Options:
|
||||
# -v, --verbose Show full paths and URLs
|
||||
|
||||
. "$psscriptroot\..\lib\buckets.ps1"
|
||||
. "$psscriptroot\..\lib\core.ps1"
|
||||
. "$psscriptroot\..\lib\depends.ps1"
|
||||
. "$psscriptroot\..\lib\help.ps1"
|
||||
. "$psscriptroot\..\lib\install.ps1"
|
||||
. "$psscriptroot\..\lib\manifest.ps1"
|
||||
. "$psscriptroot\..\lib\versions.ps1"
|
||||
. "$PSScriptRoot\..\lib\getopt.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Get-Manifest'
|
||||
. "$PSScriptRoot\..\lib\versions.ps1" # 'Get-InstalledVersion'
|
||||
|
||||
reset_aliases
|
||||
$opt, $app, $err = getopt $args 'v' 'verbose'
|
||||
if ($err) { error "scoop info: $err"; exit 1 }
|
||||
$verbose = $opt.v -or $opt.verbose
|
||||
|
||||
if(!$app) { my_usage; exit 1 }
|
||||
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
|
||||
$manifest, $bucket = 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 = manifest_path $app $bucket
|
||||
$manifest_file = if ($bucket) {
|
||||
manifest_path $app $bucket
|
||||
} else {
|
||||
$url
|
||||
}
|
||||
|
||||
$dir = versiondir $app 'current' $global
|
||||
$original_dir = versiondir $app $manifest.version $global
|
||||
$persist_dir = persistdir $app $global
|
||||
if ($verbose) {
|
||||
$dir = currentdir $app $global
|
||||
$original_dir = versiondir $app $manifest.version $global
|
||||
$persist_dir = persistdir $app $global
|
||||
} else {
|
||||
$dir, $original_dir, $persist_dir = "<root>", "<root>", "<root>"
|
||||
}
|
||||
|
||||
if($status.installed) {
|
||||
if ($status.installed) {
|
||||
$manifest_file = manifest_path $app $install.bucket
|
||||
if ($install.url) {
|
||||
$manifest_file = $install.url
|
||||
}
|
||||
if($status.version -eq $manifest.version) {
|
||||
if ($status.version -eq $manifest.version) {
|
||||
$version_output = $status.version
|
||||
} else {
|
||||
$version_output = "$($status.version) (Update to $($manifest.version) available)"
|
||||
}
|
||||
}
|
||||
|
||||
Write-Output "Name: $app"
|
||||
$item = [ordered]@{ Name = $app }
|
||||
if ($manifest.description) {
|
||||
Write-Output "Description: $($manifest.description)"
|
||||
$item.Description = $manifest.description
|
||||
}
|
||||
$item.Version = $version_output
|
||||
if ($bucket) {
|
||||
$item.Bucket = $bucket
|
||||
}
|
||||
if ($manifest.homepage) {
|
||||
$item.Website = $manifest.homepage.TrimEnd('/')
|
||||
}
|
||||
Write-Output "Version: $version_output"
|
||||
Write-Output "Website: $($manifest.homepage)"
|
||||
# Show license
|
||||
if ($manifest.license) {
|
||||
$license = $manifest.license
|
||||
if ($manifest.license.identifier -and $manifest.license.url) {
|
||||
$license = "$($manifest.license.identifier) ($($manifest.license.url))"
|
||||
$item.License = if ($manifest.license.identifier -and $manifest.license.url) {
|
||||
if ($verbose) { "$($manifest.license.identifier) ($($manifest.license.url))" } else { $manifest.license.identifier }
|
||||
} elseif ($manifest.license -match '^((ht)|f)tps?://') {
|
||||
$license = "$($manifest.license)"
|
||||
$manifest.license
|
||||
} elseif ($manifest.license -match '[|,]') {
|
||||
$licurl = $manifest.license.Split("|,") | ForEach-Object {"https://spdx.org/licenses/$_.html"}
|
||||
$license = "$($manifest.license) ($($licurl -join ', '))"
|
||||
if ($verbose) {
|
||||
"$($manifest.license) ($(($manifest.license -Split "\||," | ForEach-Object { "https://spdx.org/licenses/$_.html" }) -join ', '))"
|
||||
} else {
|
||||
$manifest.license
|
||||
}
|
||||
} else {
|
||||
$license = "$($manifest.license) (https://spdx.org/licenses/$($manifest.license).html)"
|
||||
if ($verbose) { "$($manifest.license) (https://spdx.org/licenses/$($manifest.license).html)" } else { $manifest.license }
|
||||
}
|
||||
}
|
||||
|
||||
if ($manifest.depends) {
|
||||
$item.Dependencies = $manifest.depends -join ' | '
|
||||
}
|
||||
|
||||
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 '#'
|
||||
}
|
||||
if ($gitinfo) {
|
||||
$item.'Updated at' = $gitinfo[0] | Get-Date
|
||||
$item.'Updated by' = $gitinfo[1]
|
||||
} else {
|
||||
$item.'Updated at' = (Get-Item $manifest_file).LastWriteTime
|
||||
$item.'Updated by' = (Get-Acl $manifest_file).Owner.Split('\')[-1]
|
||||
}
|
||||
Write-Output "License: $license"
|
||||
}
|
||||
|
||||
# Manifest file
|
||||
Write-Output "Manifest:`n $manifest_file"
|
||||
if ($verbose) { $item.Manifest = $manifest_file }
|
||||
|
||||
if($status.installed) {
|
||||
if ($status.installed) {
|
||||
# Show installed versions
|
||||
Write-Output "Installed:"
|
||||
$versions = versions $app $global
|
||||
$versions | ForEach-Object {
|
||||
$dir = versiondir $app $_ $global
|
||||
if($global) { $dir += " *global*" }
|
||||
Write-Output " $dir"
|
||||
$installed_output = @()
|
||||
Get-InstalledVersion -AppName $app -Global:$global | ForEach-Object {
|
||||
$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 {
|
||||
Write-Output "Installed: No"
|
||||
if ($verbose) {
|
||||
# Get download size if app not installed
|
||||
$totalPackage = 0
|
||||
foreach ($url in @(url $manifest (default_architecture))) {
|
||||
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)
|
||||
if($binaries) {
|
||||
$binary_output = "Binaries:`n "
|
||||
if ($binaries) {
|
||||
$binary_output = @()
|
||||
$binaries | ForEach-Object {
|
||||
if($_ -is [System.Array]) {
|
||||
$binary_output += " $($_[1]).exe"
|
||||
if ($_ -is [System.Array]) {
|
||||
$binary_output += "$($_[1]).$($_[0].Split('.')[-1])"
|
||||
} else {
|
||||
$binary_output += " $_"
|
||||
$binary_output += $_
|
||||
}
|
||||
}
|
||||
Write-Output $binary_output
|
||||
$item.Binaries = $binary_output -join " | "
|
||||
}
|
||||
$shortcuts = @(arch_specific 'shortcuts' $manifest $install.architecture)
|
||||
if ($shortcuts) {
|
||||
$shortcut_output = @()
|
||||
$shortcuts | ForEach-Object {
|
||||
$shortcut_output += $_[1]
|
||||
}
|
||||
$item.Shortcuts = $shortcut_output -join " | "
|
||||
}
|
||||
$env_set = arch_specific 'env_set' $manifest $install.architecture
|
||||
if ($env_set) {
|
||||
$env_vars = @()
|
||||
$env_set | Get-Member -member noteproperty | ForEach-Object {
|
||||
$env_vars += "$($_.name) = $(format $env_set.$($_.name) @{ "dir" = $dir })"
|
||||
}
|
||||
$item.Environment = $env_vars -join "`n"
|
||||
}
|
||||
$env_add_path = arch_specific 'env_add_path' $manifest $install.architecture
|
||||
if ($env_add_path) {
|
||||
$env_path = @()
|
||||
$env_add_path | Where-Object { $_ } | ForEach-Object {
|
||||
$env_path += if ($_ -eq '.') {
|
||||
$dir
|
||||
} else {
|
||||
"$dir\$_"
|
||||
}
|
||||
}
|
||||
$item.'Path Added' = $env_path -join "`n"
|
||||
}
|
||||
|
||||
if($manifest.env_set -or $manifest.env_add_path) {
|
||||
if($status.installed) {
|
||||
Write-Output "Environment:"
|
||||
} else {
|
||||
Write-Output "Environment: (simulated)"
|
||||
}
|
||||
}
|
||||
if($manifest.env_set) {
|
||||
$manifest.env_set | Get-Member -member noteproperty | ForEach-Object {
|
||||
$value = env $_.name $global
|
||||
if(!$value) {
|
||||
$value = format $manifest.env_set.$($_.name) @{ "dir" = $dir }
|
||||
}
|
||||
Write-Output " $($_.name)=$value"
|
||||
}
|
||||
}
|
||||
if($manifest.env_add_path) {
|
||||
$manifest.env_add_path | Where-Object { $_ } | ForEach-Object {
|
||||
if($_ -eq '.') {
|
||||
Write-Output " PATH=%PATH%;$dir"
|
||||
} else {
|
||||
Write-Output " PATH=%PATH%;$dir\$_"
|
||||
}
|
||||
if ($manifest.suggest) {
|
||||
$suggest_output = @()
|
||||
$manifest.suggest.PSObject.Properties | ForEach-Object {
|
||||
$suggest_output += $_.Value -join ' | '
|
||||
}
|
||||
$item.Suggestions = $suggest_output -join ' | '
|
||||
}
|
||||
|
||||
# Show notes
|
||||
show_notes $manifest $dir $original_dir $persist_dir
|
||||
if ($manifest.notes) {
|
||||
# Show notes
|
||||
$item.Notes = (substitute $manifest.notes @{ '$dir' = $dir; '$original_dir' = $original_dir; '$persist_dir' = $persist_dir }) -join "`n"
|
||||
}
|
||||
|
||||
[PSCustomObject]$item
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -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
|
||||
#
|
||||
@@ -13,41 +17,22 @@
|
||||
# -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
|
||||
|
||||
. "$psscriptroot\..\lib\core.ps1"
|
||||
. "$psscriptroot\..\lib\manifest.ps1"
|
||||
. "$psscriptroot\..\lib\buckets.ps1"
|
||||
. "$psscriptroot\..\lib\decompress.ps1"
|
||||
. "$psscriptroot\..\lib\install.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"
|
||||
. "$PSScriptRoot\..\lib\getopt.ps1"
|
||||
. "$PSScriptRoot\..\lib\json.ps1" # 'autoupdate.ps1' 'manifest.ps1' (indirectly)
|
||||
. "$PSScriptRoot\..\lib\autoupdate.ps1" # 'generate_user_manifest' (indirectly)
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1" # 'default_architecture' 'generate_user_manifest' 'Get-Manifest' 'Select-CurrentVersion' (indirectly)
|
||||
. "$PSScriptRoot\..\lib\install.ps1"
|
||||
. "$PSScriptRoot\..\lib\decompress.ps1"
|
||||
. "$PSScriptRoot\..\lib\shortcuts.ps1"
|
||||
. "$PSScriptRoot\..\lib\psmodules.ps1"
|
||||
. "$PSScriptRoot\..\lib\versions.ps1"
|
||||
. "$PSScriptRoot\..\lib\depends.ps1"
|
||||
|
||||
reset_aliases
|
||||
|
||||
function is_installed($app, $global) {
|
||||
if ($app.EndsWith('.json')) {
|
||||
$app = [System.IO.Path]::GetFileNameWithoutExtension($app)
|
||||
}
|
||||
if (installed $app $global) {
|
||||
function gf($g) { if ($g) { ' --global' } }
|
||||
|
||||
$version = @(versions $app $global)[-1]
|
||||
if (!(install_info $app $version $global)) {
|
||||
error "It looks like a previous installation of $app failed.`nRun 'scoop uninstall $app$(gf $global)' before retrying the install."
|
||||
}
|
||||
warn "'$app' ($version) is already installed.`nUse 'scoop update $app$(gf $global)' to install a new version."
|
||||
return $true
|
||||
}
|
||||
return $false
|
||||
}
|
||||
|
||||
$opt, $apps, $err = getopt $args 'gfiksa:' 'global', 'force', 'independent', 'no-cache', 'skip', 'arch='
|
||||
$opt, $apps, $err = getopt $args 'gikusa:' 'global', 'independent', 'no-cache', 'no-update-scoop', 'skip', 'arch='
|
||||
if ($err) { "scoop install: $err"; exit 1 }
|
||||
|
||||
$global = $opt.g -or $opt.global
|
||||
@@ -68,13 +53,24 @@ if ($global -and !(is_admin)) {
|
||||
}
|
||||
|
||||
if (is_scoop_outdated) {
|
||||
scoop update
|
||||
if ($opt.u -or $opt.'no-update-scoop') {
|
||||
warn "Scoop is out of date."
|
||||
} else {
|
||||
scoop update
|
||||
}
|
||||
}
|
||||
|
||||
ensure_none_failed $apps
|
||||
|
||||
if ($apps.length -eq 1) {
|
||||
$app, $null, $version = parse_app $apps
|
||||
if ($null -eq $version -and (is_installed $app $global)) {
|
||||
return
|
||||
if ($app.EndsWith('.json')) {
|
||||
$app = [System.IO.Path]::GetFileNameWithoutExtension($app)
|
||||
}
|
||||
$curVersion = Select-CurrentVersion -AppName $app -Global:$global
|
||||
if ($null -eq $version -and $curVersion) {
|
||||
warn "'$app' ($curVersion) is already installed.`nUse 'scoop update $app$(if ($global) { ' --global' })' to install a new version."
|
||||
exit 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +90,8 @@ if ($specific_versions.length -gt 0) {
|
||||
$specific_versions_paths = $specific_versions | ForEach-Object {
|
||||
$app, $bucket, $version = parse_app $_
|
||||
if (installed_manifest $app $version) {
|
||||
abort "'$app' ($version) is already installed.`nUse 'scoop update $app$global_flag' to install a new version."
|
||||
warn "'$app' ($version) is already installed.`nUse 'scoop update $app$(if ($global) { " --global" })' to install a new version."
|
||||
continue
|
||||
}
|
||||
|
||||
generate_user_manifest $app $bucket $version
|
||||
@@ -106,22 +103,23 @@ $apps = @(($specific_versions_paths + $difference) | Where-Object { $_ } | Sort-
|
||||
$explicit_apps = $apps
|
||||
|
||||
if (!$independent) {
|
||||
$apps = install_order $apps $architecture # adds dependencies
|
||||
$apps = $apps | Get-Dependency -Architecture $architecture | Select-Object -Unique # adds dependencies
|
||||
}
|
||||
ensure_none_failed $apps $global
|
||||
ensure_none_failed $apps
|
||||
|
||||
$apps, $skip = prune_installed $apps $global
|
||||
|
||||
$skip | Where-Object { $explicit_apps -contains $_ } | ForEach-Object {
|
||||
$app, $null, $null = parse_app $_
|
||||
$version = @(versions $app $global)[-1]
|
||||
$version = Select-CurrentVersion -AppName $app -Global:$global
|
||||
warn "'$app' ($version) is already installed. Skipping."
|
||||
}
|
||||
|
||||
$suggested = @{ };
|
||||
if (Test-Aria2Enabled) {
|
||||
if ((Test-Aria2Enabled) -and (get_config 'aria2-warning-enabled' $true)) {
|
||||
warn "Scoop uses 'aria2c' for multi-connection downloads."
|
||||
warn "Should it cause issues, run 'scoop config aria2-enabled false' to disable it."
|
||||
warn "To disable this warning, run 'scoop config aria2-warning-enabled false'."
|
||||
}
|
||||
$apps | ForEach-Object { install_app $_ $architecture $global $suggested $use_cache $check_hash }
|
||||
|
||||
|
||||
@@ -3,49 +3,60 @@
|
||||
# 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
|
||||
if (-not (Get-FormatData ScoopApps)) {
|
||||
Update-FormatData "$PSScriptRoot\..\supporting\formats\ScoopTypes.Format.ps1xml"
|
||||
}
|
||||
|
||||
$local = installed_apps $false | ForEach-Object { @{ name = $_ } }
|
||||
$global = installed_apps $true | ForEach-Object { @{ name = $_; global = $true } }
|
||||
|
||||
$apps = @($local) + @($global)
|
||||
|
||||
if($apps) {
|
||||
write-host "Installed apps$(if($query) { `" matching '$query'`"}): `n"
|
||||
$apps | Sort-Object { $_.name } | Where-Object { !$query -or ($_.name -match $query) } | ForEach-Object {
|
||||
$app = $_.name
|
||||
$global = $_.global
|
||||
$ver = current_version $app $global
|
||||
|
||||
$install_info = install_info $app $ver $global
|
||||
write-host " $app " -NoNewline
|
||||
write-host -f DarkCyan $ver -NoNewline
|
||||
|
||||
if($global) { write-host -f DarkGreen ' *global*' -NoNewline }
|
||||
|
||||
if (!$install_info) { Write-Host ' *failed*' -ForegroundColor DarkRed -NoNewline }
|
||||
if ($install_info.hold) { Write-Host ' *hold*' -ForegroundColor DarkMagenta -NoNewline }
|
||||
|
||||
if ($install_info.bucket -and ($install_info.bucket -ne 'main')) {
|
||||
write-host -f Yellow " [$($install_info.bucket)]" -NoNewline
|
||||
} elseif ($install_info.url) {
|
||||
write-host -f Yellow " [$($install_info.url)]" -NoNewline
|
||||
}
|
||||
|
||||
if ($install_info.architecture -and $def_arch -ne $install_info.architecture) {
|
||||
write-host -f DarkRed " {$($install_info.architecture)}" -NoNewline
|
||||
}
|
||||
write-host ''
|
||||
}
|
||||
write-host ''
|
||||
exit 0
|
||||
} else {
|
||||
write-host "There aren't any apps installed."
|
||||
if (-not $apps) {
|
||||
Write-Host "There aren't any apps installed."
|
||||
exit 1
|
||||
}
|
||||
|
||||
$list = @()
|
||||
Write-Host "Installed apps$(if($query) { `" matching '$query'`"}):"
|
||||
$apps | Where-Object { !$query -or ($_.name -match $query) } | ForEach-Object {
|
||||
$app = $_.name
|
||||
$global = $_.global
|
||||
$item = @{}
|
||||
$ver = Select-CurrentVersion -AppName $app -Global:$global
|
||||
$item.Name = $app
|
||||
$item.Version = $ver
|
||||
|
||||
$install_info_path = "$(versiondir $app $ver $global)\install.json"
|
||||
$updated = (Get-Item (appdir $app $global)).LastWriteTime
|
||||
$install_info = $null
|
||||
if (Test-Path $install_info_path) {
|
||||
$install_info = parse_json $install_info_path
|
||||
$updated = (Get-Item $install_info_path).LastWriteTime
|
||||
}
|
||||
|
||||
$item.Source = if ($install_info.bucket) {
|
||||
$install_info.bucket
|
||||
} elseif ($install_info.url) {
|
||||
if ($install_info.url -eq (usermanifest $app)) { '<auto-generated>' }
|
||||
else { $install_info.url }
|
||||
}
|
||||
$item.Updated = $updated
|
||||
|
||||
$info = @()
|
||||
if ($global) { $info += 'Global install' }
|
||||
if (failed $app $global) { $info += 'Install failed' }
|
||||
if ($install_info.hold) { $info += 'Held package' }
|
||||
if ($install_info.architecture -and $def_arch -ne $install_info.architecture) {
|
||||
$info += $install_info.architecture
|
||||
}
|
||||
$item.Info = $info -join ', '
|
||||
|
||||
$list += [PSCustomObject]$item
|
||||
}
|
||||
|
||||
$list | Add-Member -TypeName 'ScoopApps' -PassThru
|
||||
exit 0
|
||||
|
||||
@@ -2,21 +2,19 @@
|
||||
# Summary: Returns the path to the specified app
|
||||
param($app)
|
||||
|
||||
. "$psscriptroot\..\lib\core.ps1"
|
||||
. "$psscriptroot\..\lib\help.ps1"
|
||||
. "$psscriptroot\..\lib\manifest.ps1"
|
||||
. "$psscriptroot\..\lib\buckets.ps1"
|
||||
. "$PSScriptRoot\..\lib\versions.ps1" # 'currentdir' (indirectly)
|
||||
|
||||
reset_aliases
|
||||
|
||||
if(!$app) { my_usage; exit 1 }
|
||||
|
||||
$app_path = versiondir $app 'current' $false
|
||||
if(!(Test-Path $app_path)) {
|
||||
$app_path = versiondir $app 'current' $true
|
||||
if (!$app) {
|
||||
my_usage
|
||||
exit 1
|
||||
}
|
||||
|
||||
if(Test-Path $app_path) {
|
||||
$app_path = currentdir $app $false
|
||||
if (!(Test-Path $app_path)) {
|
||||
$app_path = currentdir $app $true
|
||||
}
|
||||
|
||||
if (Test-Path $app_path) {
|
||||
Write-Output $app_path
|
||||
} else {
|
||||
abort "Could not find app path for '$app'."
|
||||
|
||||
@@ -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\install.ps1"
|
||||
. "$psscriptroot\..\lib\versions.ps1"
|
||||
. "$psscriptroot\..\lib\shortcuts.ps1"
|
||||
. "$PSScriptRoot\..\lib\getopt.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Select-CurrentVersion' (indirectly)
|
||||
. "$PSScriptRoot\..\lib\install.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)
|
||||
@@ -45,7 +45,7 @@ $apps | ForEach-Object {
|
||||
}
|
||||
|
||||
if ($null -eq $version) {
|
||||
$version = current_version $app $global
|
||||
$version = Select-CurrentVersion -AppName $app -Global:$global
|
||||
}
|
||||
|
||||
$manifest = installed_manifest $app $version $global
|
||||
@@ -67,16 +67,22 @@ $apps | ForEach-Object {
|
||||
$original_dir = $dir
|
||||
$persist_dir = persistdir $app $global
|
||||
|
||||
#region Workaround for #2952
|
||||
if (test_running_process $app $global) {
|
||||
continue
|
||||
}
|
||||
#endregion Workaround for #2952
|
||||
|
||||
$install = install_info $app $version $global
|
||||
$architecture = $install.architecture
|
||||
|
||||
$dir = link_current $dir
|
||||
create_shims $manifest $dir $global $architecture
|
||||
create_startmenu_shortcuts $manifest $dir $global $architecture
|
||||
env_add_path $manifest $dir
|
||||
env_set $manifest $dir $global
|
||||
env_add_path $manifest $dir $global $architecture
|
||||
env_set $manifest $dir $global $architecture
|
||||
# unlink all potential old link before re-persisting
|
||||
unlink_persist_data $original_dir
|
||||
unlink_persist_data $manifest $original_dir
|
||||
persist_data $manifest $original_dir $persist_dir
|
||||
persist_permission $manifest $global
|
||||
}
|
||||
|
||||
@@ -5,71 +5,79 @@
|
||||
# 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
|
||||
}
|
||||
}
|
||||
}
|
||||
$apps | ForEach-Object { $_.version = (latest_version $_.name $bucket); $_ }
|
||||
$apps | ForEach-Object { $_.version = (Get-LatestVersion -AppName $_.name -Bucket $bucket); $_ }
|
||||
}
|
||||
|
||||
function download_json($url) {
|
||||
$progressPreference = 'silentlycontinue'
|
||||
$result = invoke-webrequest $url -UseBasicParsing | Select-Object -exp content | convertfrom-json
|
||||
$progressPreference = 'continue'
|
||||
$ProgressPreference = 'SilentlyContinue'
|
||||
$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
|
||||
$api_link = 'https://api.github.com/rate_limit'
|
||||
$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) {
|
||||
$repo = known_bucket_repo $bucket
|
||||
|
||||
$uri = [system.uri]($repo)
|
||||
if ($uri.absolutepath -match '/([a-zA-Z0-9]*)/([a-zA-Z0-9-]*)(.git|/)?') {
|
||||
$user = $matches[1]
|
||||
$repo_name = $matches[2]
|
||||
$uri = [System.Uri](known_bucket_repo $bucket)
|
||||
if ($uri.AbsolutePath -match '/([a-zA-Z0-9]*)/([a-zA-Z0-9-]*)(?:.git|/)?') {
|
||||
$user = $Matches[1]
|
||||
$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 -exp tree | Where-Object {
|
||||
$_.path -match "(^(.*$query.*).json$)"
|
||||
} | ForEach-Object { $matches[2] }
|
||||
$result = download_json $api_link | Select-Object -ExpandProperty tree |
|
||||
Where-Object -Value "^bucket/(.*$query.*)\.json$" -Property Path -Match |
|
||||
ForEach-Object { $Matches[1] }
|
||||
}
|
||||
|
||||
$result
|
||||
@@ -77,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:"
|
||||
$_.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
|
||||
}
|
||||
|
||||
|
||||
226
libexec/scoop-shim.ps1
Normal file
226
libexec/scoop-shim.ps1
Normal file
@@ -0,0 +1,226 @@
|
||||
# Usage: scoop shim <subcommand> [<shim_names>] [<command_path> [<args>...]] [-g(lobal)]
|
||||
# Summary: Manipulate Scoop shims
|
||||
# Help: Manipulate Scoop shims: add, rm, list, info, alter, etc.
|
||||
#
|
||||
# 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):
|
||||
#
|
||||
# scoop shim rm <shim_names>
|
||||
#
|
||||
# To list all shims or matching shims, use the 'list' subcommand:
|
||||
#
|
||||
# scoop shim list [<shim_names>]
|
||||
#
|
||||
# To show a shim's information, use the 'info' subcommand:
|
||||
#
|
||||
# scoop shim info <shim_name>
|
||||
#
|
||||
# To alternate a shim's target source, use the 'alter' subcommand:
|
||||
#
|
||||
# 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)
|
||||
|
||||
param($SubCommand, $ShimName, [Switch]$global)
|
||||
|
||||
. "$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 (-not (Get-FormatData ScoopShims)) {
|
||||
Update-FormatData "$PSScriptRoot\..\supporting\formats\ScoopTypes.Format.ps1xml"
|
||||
}
|
||||
|
||||
$localShimDir = shimdir $false
|
||||
$globalShimDir = shimdir $true
|
||||
|
||||
function Get-ShimInfo($ShimPath) {
|
||||
$info = [Ordered]@{}
|
||||
$info.Name = strip_ext (fname $ShimPath)
|
||||
$info.Path = $ShimPath -replace 'shim$', 'exe'
|
||||
$info.Source = (get_app_name_from_shim $ShimPath) -replace '^$', 'External'
|
||||
$info.Type = if ($ShimPath.EndsWith('.ps1')) { 'ExternalScript' } else { 'Application' }
|
||||
$altShims = Get-Item -Path "$ShimPath.*" -Exclude '*.shim', '*.cmd', '*.ps1'
|
||||
if ($altShims) {
|
||||
$info.Alternatives = (@($info.Source) + ($altShims | ForEach-Object { $_.Extension.Remove(0, 1) } | Select-Object -Unique)) -join ' '
|
||||
}
|
||||
$info.IsGlobal = $ShimPath.StartsWith("$globalShimDir")
|
||||
$info.IsHidden = !((Get-Command -Name $info.Name).Path -eq $info.Path)
|
||||
[PSCustomObject]$info
|
||||
}
|
||||
|
||||
function Get-ShimPath($ShimName, $Global) {
|
||||
'.shim', '.ps1' | ForEach-Object {
|
||||
$shimPath = Join-Path (shimdir $Global) "$ShimName$_"
|
||||
if (Test-Path $shimPath) {
|
||||
return $shimPath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch ($SubCommand) {
|
||||
'add' {
|
||||
if ($commandPath -notmatch '[\\/]') {
|
||||
$shortPath = $commandPath
|
||||
$commandPath = Get-ShimTarget (Get-ShimPath $shortPath $global)
|
||||
if (!$commandPath) {
|
||||
$exCommand = Get-Command $shortPath -ErrorAction SilentlyContinue
|
||||
if ($exCommand -and $exCommand.CommandType -eq 'Application') {
|
||||
$commandPath = $exCommand.Path
|
||||
} # TODO - add support for more command types: Alias, Cmdlet, ExternalScript, Filter, Function, Script, and Workflow
|
||||
}
|
||||
}
|
||||
if ($commandPath -and (Test-Path $commandPath)) {
|
||||
Write-Host "Adding $(if ($global) { 'global' } else { 'local' }) shim " -NoNewline
|
||||
Write-Host $shimName -ForegroundColor Cyan -NoNewline
|
||||
Write-Host '...'
|
||||
shim $commandPath $global $shimName $commandArgs
|
||||
} else {
|
||||
abort "ERROR: '$($Args[0])' does not exist" 3
|
||||
}
|
||||
}
|
||||
'rm' {
|
||||
$failed = @()
|
||||
$ShimName | ForEach-Object {
|
||||
if (Get-ShimPath $_ $global) {
|
||||
rm_shim $_ (shimdir $global)
|
||||
} else {
|
||||
$failed += $_
|
||||
}
|
||||
}
|
||||
if ($failed) {
|
||||
Write-Host 'Shims not found: ' -NoNewline
|
||||
Write-Host $failed -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
|
||||
if (Test-Path $globalShimDir) {
|
||||
$shims += Get-ChildItem -Path $globalShimDir -Recurse -Include '*.shim', '*.ps1' |
|
||||
Where-Object { !$ShimName -or ($_.BaseName -match $ShimName) } |
|
||||
Select-Object -ExpandProperty FullName
|
||||
}
|
||||
$shims.ForEach({ Get-ShimInfo $_ }) | Add-Member -TypeName 'ScoopShims' -PassThru
|
||||
}
|
||||
'info' {
|
||||
$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 "But a $(if ($global) { 'local' } else {'global' }) shim exists, " -NoNewline
|
||||
Write-Host "run 'scoop shim info $ShimName$(if (!$global) { ' -global' })' to show its info"
|
||||
exit 2
|
||||
}
|
||||
exit 3
|
||||
}
|
||||
}
|
||||
'alter' {
|
||||
$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.'
|
||||
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])."
|
||||
}
|
||||
$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 ' 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 ' from ' -NoNewline
|
||||
Write-Host $newApp -ForegroundColor DarkYellow -NoNewline
|
||||
Write-Host ' as default...' -NoNewline
|
||||
$pathNoExt = strip_ext $shimPath
|
||||
'', '.shim', '.cmd', '.ps1' | ForEach-Object {
|
||||
$oldShimPath = "$pathNoExt$_"
|
||||
$newShimPath = "$oldShimPath.$newApp"
|
||||
if (Test-Path -Path $oldShimPath -PathType Leaf) {
|
||||
Rename-Item -Path $oldShimPath -NewName "$oldShimPath.$($shimInfo.Source)" -Force
|
||||
if (Test-Path -Path $newShimPath -PathType Leaf) {
|
||||
Rename-Item -Path $newShimPath -NewName $oldShimPath -Force
|
||||
}
|
||||
}
|
||||
}
|
||||
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 "But a $(if ($global) { 'local' } else {'global' }) shim exists, " -NoNewline
|
||||
Write-Host "run 'scoop shim alter $ShimName$(if (!$global) { ' -global' })' to alternate its source"
|
||||
exit 2
|
||||
}
|
||||
exit 3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exit 0
|
||||
@@ -1,107 +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"
|
||||
. "$psscriptroot\..\lib\git.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") {
|
||||
Push-Location $currentdir
|
||||
git_fetch -q origin
|
||||
$commits = $(git log "HEAD..origin/$(scoop config SCOOP_BRANCH)" --oneline)
|
||||
if($commits) { $needs_update = $true }
|
||||
Pop-Location
|
||||
}
|
||||
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") {
|
||||
git_cmd -C "`"$repopath`"" fetch -q origin
|
||||
$script:network_failure = 128 -eq $LASTEXITCODE
|
||||
$branch = git -C $repopath branch --show-current
|
||||
$commits = git -C $repopath log "HEAD..origin/$branch" --oneline
|
||||
if ($commits) { return $true }
|
||||
else { return $false }
|
||||
} else {
|
||||
return $true
|
||||
}
|
||||
}
|
||||
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,33 +1,58 @@
|
||||
# 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\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 (!(installed $app $global)) {
|
||||
if ($global) {
|
||||
error "'$app' is not installed globally."
|
||||
} else {
|
||||
error "'$app' is not installed."
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
$dir = versiondir $app 'current' $global
|
||||
$json = install_info $app 'current' $global
|
||||
if (get_config NO_JUNCTIONS) {
|
||||
$version = Select-CurrentVersion -App $app -Global:$global
|
||||
} else {
|
||||
$version = 'current'
|
||||
}
|
||||
$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 now unlocked and can be updated again."
|
||||
success "$app is no longer held and can be updated again."
|
||||
}
|
||||
|
||||
exit $exitcode
|
||||
|
||||
@@ -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'
|
||||
@@ -50,73 +46,79 @@ if (!$apps) { exit 0 }
|
||||
:app_loop foreach ($_ in $apps) {
|
||||
($app, $global) = $_
|
||||
|
||||
$version = current_version $app $global
|
||||
Write-Host "Uninstalling '$app' ($version)."
|
||||
$version = Select-CurrentVersion -AppName $app -Global:$global
|
||||
$appDir = appdir $app $global
|
||||
if ($version) {
|
||||
Write-Host "Uninstalling '$app' ($version)."
|
||||
|
||||
$dir = versiondir $app $version $global
|
||||
$persist_dir = persistdir $app $global
|
||||
$dir = versiondir $app $version $global
|
||||
$persist_dir = persistdir $app $global
|
||||
|
||||
#region Workaround for #2952
|
||||
$processdir = appdir $app $global | Resolve-Path | Select-Object -ExpandProperty Path
|
||||
if (Get-Process | Where-Object { $_.Path -like "$processdir\*" }) {
|
||||
error "Application is still running. Close all instances and try again."
|
||||
continue
|
||||
}
|
||||
#endregion Workaround for #2952
|
||||
$manifest = installed_manifest $app $version $global
|
||||
$install = install_info $app $version $global
|
||||
$architecture = $install.architecture
|
||||
|
||||
try {
|
||||
Test-Path $dir -ErrorAction Stop | Out-Null
|
||||
} catch [UnauthorizedAccessException] {
|
||||
error "Access denied: $dir. You might need to restart."
|
||||
continue
|
||||
}
|
||||
Invoke-HookScript -HookType 'pre_uninstall' -Manifest $manifest -Arch $architecture
|
||||
|
||||
$manifest = installed_manifest $app $version $global
|
||||
$install = install_info $app $version $global
|
||||
$architecture = $install.architecture
|
||||
|
||||
run_uninstaller $manifest $architecture $dir
|
||||
rm_shims $manifest $global $architecture
|
||||
rm_startmenu_shortcuts $manifest $global $architecture
|
||||
|
||||
# If a junction was used during install, that will have been used
|
||||
# as the reference directory. Otherwise it will just be the version
|
||||
# directory.
|
||||
$refdir = unlink_current $dir
|
||||
|
||||
uninstall_psmodule $manifest $refdir $global
|
||||
|
||||
env_rm_path $manifest $refdir $global
|
||||
env_rm $manifest $global
|
||||
|
||||
try {
|
||||
# unlink all potential old link before doing recursive Remove-Item
|
||||
unlink_persist_data $dir
|
||||
Remove-Item $dir -Recurse -Force -ErrorAction Stop
|
||||
} catch {
|
||||
if (Test-Path $dir) {
|
||||
error "Couldn't remove '$(friendly_path $dir)'; it may be in use."
|
||||
#region Workaround for #2952
|
||||
if (test_running_process $app $global) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
#endregion Workaround for #2952
|
||||
|
||||
try {
|
||||
Test-Path $dir -ErrorAction Stop | Out-Null
|
||||
} catch [UnauthorizedAccessException] {
|
||||
error "Access denied: $dir. You might need to restart."
|
||||
continue
|
||||
}
|
||||
|
||||
run_uninstaller $manifest $architecture $dir
|
||||
rm_shims $app $manifest $global $architecture
|
||||
rm_startmenu_shortcuts $manifest $global $architecture
|
||||
|
||||
# If a junction was used during install, that will have been used
|
||||
# as the reference directory. Otherwise it will just be the version
|
||||
# directory.
|
||||
$refdir = unlink_current $dir
|
||||
|
||||
uninstall_psmodule $manifest $refdir $global
|
||||
|
||||
env_rm_path $manifest $refdir $global $architecture
|
||||
env_rm $manifest $global $architecture
|
||||
|
||||
# remove older versions
|
||||
$old = @(versions $app $global)
|
||||
foreach ($oldver in $old) {
|
||||
Write-Host "Removing older version ($oldver)."
|
||||
$dir = versiondir $app $oldver $global
|
||||
try {
|
||||
# unlink all potential old link before doing recursive Remove-Item
|
||||
unlink_persist_data $dir
|
||||
unlink_persist_data $manifest $dir
|
||||
Remove-Item $dir -Recurse -Force -ErrorAction Stop
|
||||
} catch {
|
||||
if (Test-Path $dir) {
|
||||
error "Couldn't remove '$(friendly_path $dir)'; it may be in use."
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
Invoke-HookScript -HookType 'post_uninstall' -Manifest $manifest -Arch $architecture
|
||||
}
|
||||
# remove older versions
|
||||
$oldVersions = @(Get-ChildItem $appDir -Name -Exclude 'current')
|
||||
foreach ($version in $oldVersions) {
|
||||
Write-Host "Removing older version ($version)."
|
||||
$dir = versiondir $app $version $global
|
||||
try {
|
||||
# unlink all potential old link before doing recursive Remove-Item
|
||||
unlink_persist_data $manifest $dir
|
||||
Remove-Item $dir -Recurse -Force -ErrorAction Stop
|
||||
} catch {
|
||||
error "Couldn't remove '$(friendly_path $dir)'; it may be in use."
|
||||
continue app_loop
|
||||
}
|
||||
}
|
||||
|
||||
if (@(versions $app $global).length -eq 0) {
|
||||
$appdir = appdir $app $global
|
||||
if (Test-Path ($currentDir = Join-Path $appDir 'current')) {
|
||||
attrib $currentDir -R /L
|
||||
Remove-Item $currentDir -ErrorAction Stop -Force
|
||||
}
|
||||
if (!(Get-ChildItem $appDir)) {
|
||||
try {
|
||||
# if last install failed, the directory seems to be locked and this
|
||||
# will throw an error about the directory not existing
|
||||
|
||||
@@ -12,22 +12,19 @@
|
||||
# -k, --no-cache Don't use the download cache
|
||||
# -s, --skip Skip hash validation (use with caution!)
|
||||
# -q, --quiet Hide extraneous messages
|
||||
# -a, --all Update all apps (alternative to '*')
|
||||
|
||||
. "$psscriptroot\..\lib\core.ps1"
|
||||
. "$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\git.ps1"
|
||||
. "$psscriptroot\..\lib\install.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\versions.ps1"
|
||||
. "$PSScriptRoot\..\lib\depends.ps1"
|
||||
. "$PSScriptRoot\..\lib\install.ps1"
|
||||
|
||||
reset_aliases
|
||||
|
||||
$opt, $apps, $err = getopt $args 'gfiksq:' 'global', 'force', 'independent', 'no-cache', 'skip', 'quiet'
|
||||
$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
|
||||
$force = $opt.f -or $opt.force
|
||||
@@ -35,11 +32,12 @@ $check_hash = !($opt.s -or $opt.skip)
|
||||
$use_cache = !($opt.k -or $opt.'no-cache')
|
||||
$quiet = $opt.q -or $opt.quiet
|
||||
$independent = $opt.i -or $opt.independent
|
||||
$all = $opt.a -or $opt.all
|
||||
|
||||
# load config
|
||||
$configRepo = get_config SCOOP_REPO
|
||||
if (!$configRepo) {
|
||||
$configRepo = "https://github.com/lukesampson/scoop"
|
||||
$configRepo = "https://github.com/ScoopInstaller/Scoop"
|
||||
set_config SCOOP_REPO $configRepo | Out-Null
|
||||
}
|
||||
|
||||
@@ -52,106 +50,120 @@ if (!$configBranch) {
|
||||
|
||||
if(($PSVersionTable.PSVersion.Major) -lt 5) {
|
||||
# check powershell version
|
||||
# should be deleted after Oct 1, 2019
|
||||
If ((Get-Date).ToUniversalTime() -ge "2019-10-01") {
|
||||
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
|
||||
} else {
|
||||
Write-Output "Scoop is going to stop supporting old version of PowerShell."
|
||||
Write-Output "Please upgrade to PowerShell 5 or later version before Oct 1, 2019 UTC."
|
||||
Write-Output "Guideline: https://docs.microsoft.com/en-us/powershell/scripting/setup/installing-windows-powershell"
|
||||
}
|
||||
Write-Output "PowerShell 5 or later is required to run Scoop."
|
||||
Write-Output "Upgrade PowerShell: https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell-core-on-windows"
|
||||
break
|
||||
}
|
||||
|
||||
function update_scoop() {
|
||||
# check for git
|
||||
if(!(Test-CommandAvailable git)) { abort "Scoop uses Git to update itself. Run 'scoop install git' and try again." }
|
||||
|
||||
write-host "Updating Scoop..."
|
||||
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')
|
||||
if (!(Test-Path "$currentdir\.git")) {
|
||||
$newdir = "$currentdir\..\new"
|
||||
$olddir = "$currentdir\..\old"
|
||||
|
||||
# get git scoop
|
||||
git_clone -q $configRepo --branch $configBranch --single-branch "`"$newdir`""
|
||||
git_cmd 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 {
|
||||
Push-Location $currentdir
|
||||
|
||||
$currentRepo = git_config remote.origin.url
|
||||
$currentBranch = git_branch
|
||||
$previousCommit = git -C "$currentdir" rev-parse HEAD
|
||||
$currentRepo = git -C "$currentdir" config remote.origin.url
|
||||
$currentBranch = git -C "$currentdir" branch
|
||||
|
||||
$isRepoChanged = !($currentRepo -match $configRepo)
|
||||
$isBranchChanged = !($currentBranch -match "\*\s+$configBranch")
|
||||
|
||||
# Change remote url if the repo is changed
|
||||
if ($isRepoChanged) {
|
||||
git_config remote.origin.url "$configRepo"
|
||||
git -C "$currentdir" config remote.origin.url "$configRepo"
|
||||
}
|
||||
|
||||
# Fetch and reset local repo if the repo or the branch is changed
|
||||
if ($isRepoChanged -or $isBranchChanged) {
|
||||
# Reset git fetch refs, so that it can fetch all branches (GH-3368)
|
||||
git_config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"
|
||||
git -C "$currentdir" config remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*'
|
||||
# fetch remote branch
|
||||
git_fetch --force origin "refs/heads/`"$configBranch`":refs/remotes/origin/$configBranch" -q
|
||||
git_cmd -C "`"$currentdir`"" fetch --force origin "refs/heads/`"$configBranch`":refs/remotes/origin/$configBranch" -q
|
||||
# checkout and track the branch
|
||||
git_checkout -B $configBranch -t origin/$configBranch -q
|
||||
git_cmd -C "`"$currentdir`"" checkout -B $configBranch -t origin/$configBranch -q
|
||||
# reset branch HEAD
|
||||
git_reset --hard origin/$configBranch -q
|
||||
git -C "$currentdir" reset --hard origin/$configBranch -q
|
||||
} else {
|
||||
git_pull -q
|
||||
git_cmd -C "`"$currentdir`"" pull -q
|
||||
}
|
||||
|
||||
$res = $lastexitcode
|
||||
if ($show_update_log) {
|
||||
git_log --no-decorate --date=local --since="`"$last_update`"" --format="`"tformat: * %C(yellow)%h%Creset %<|(72,trunc)%s %C(cyan)%cr%Creset`"" HEAD
|
||||
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"
|
||||
}
|
||||
|
||||
Pop-Location
|
||||
if ($res -ne 0) {
|
||||
abort 'Update failed.'
|
||||
}
|
||||
}
|
||||
|
||||
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'
|
||||
}
|
||||
# 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'
|
||||
# }
|
||||
|
||||
ensure_scoop_in_path
|
||||
shim "$currentdir\bin\scoop.ps1" $false
|
||||
|
||||
Get-LocalBucket | ForEach-Object {
|
||||
write-host "Updating '$_' bucket..."
|
||||
foreach ($bucket in Get-LocalBucket) {
|
||||
Write-Host "Updating '$bucket' bucket..."
|
||||
|
||||
$loc = Find-BucketDirectory $_ -Root
|
||||
# Make sure main bucket, which was downloaded as zip, will be properly "converted" into git
|
||||
if (($_ -eq 'main') -and !(Test-Path "$loc\.git")) {
|
||||
rm_bucket 'main'
|
||||
add_bucket 'main'
|
||||
$bucketLoc = Find-BucketDirectory $bucket -Root
|
||||
|
||||
if (!(Test-Path (Join-Path $bucketLoc '.git'))) {
|
||||
if ($bucket -eq 'main') {
|
||||
# Make sure main bucket, which was downloaded as zip, will be properly "converted" into git
|
||||
Write-Host " Converting 'main' bucket to git repo..."
|
||||
$status = rm_bucket 'main'
|
||||
if ($status -ne 0) {
|
||||
abort "Failed to remove local 'main' bucket."
|
||||
}
|
||||
$status = add_bucket 'main' (known_bucket_repo 'main')
|
||||
if ($status -ne 0) {
|
||||
abort "Failed to add remote 'main' bucket."
|
||||
}
|
||||
} else {
|
||||
Write-Host "'$bucket' is not a git repository. Skipped."
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
Push-Location $loc
|
||||
git_pull -q
|
||||
$previousCommit = git -C "$bucketLoc" rev-parse HEAD
|
||||
git_cmd -C "`"$bucketLoc`"" pull -q
|
||||
if ($show_update_log) {
|
||||
git_log --no-decorate --date=local --since="`"$last_update`"" --format="`"tformat: * %C(yellow)%h%Creset %<|(72,trunc)%s %C(cyan)%cr%Creset`"" HEAD
|
||||
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"
|
||||
}
|
||||
Pop-Location
|
||||
}
|
||||
|
||||
set_config lastupdate ([System.DateTime]::Now.ToString('o')) | Out-Null
|
||||
@@ -159,7 +171,7 @@ function update_scoop() {
|
||||
}
|
||||
|
||||
function update($app, $global, $quiet = $false, $independent, $suggested, $use_cache = $true, $check_hash = $true) {
|
||||
$old_version = current_version $app $global
|
||||
$old_version = Select-CurrentVersion -AppName $app -Global:$global
|
||||
$old_manifest = installed_manifest $app $old_version $global
|
||||
$install = install_info $app $old_version $global
|
||||
|
||||
@@ -171,14 +183,8 @@ function update($app, $global, $quiet = $false, $independent, $suggested, $use_c
|
||||
}
|
||||
$url = $install.url
|
||||
|
||||
if (!$independent) {
|
||||
# check dependencies
|
||||
$man = if ($url) { $url } else { $app }
|
||||
$deps = @(deps $man $architecture) | Where-Object { !(installed $_) }
|
||||
$deps | ForEach-Object { install_app $_ $architecture $global $suggested $use_cache $check_hash }
|
||||
}
|
||||
|
||||
$version = latest_version $app $bucket $url
|
||||
$manifest = manifest $app $bucket $url
|
||||
$version = $manifest.version
|
||||
$is_nightly = $version -eq 'nightly'
|
||||
if ($is_nightly) {
|
||||
$version = nightly_version $(get-date) $quiet
|
||||
@@ -197,18 +203,16 @@ function update($app, $global, $quiet = $false, $independent, $suggested, $use_c
|
||||
return
|
||||
}
|
||||
|
||||
$manifest = manifest $app $bucket $url
|
||||
|
||||
write-host "Updating '$app' ($old_version -> $version)"
|
||||
Write-Host "Updating '$app' ($old_version -> $version)"
|
||||
|
||||
# region Workaround
|
||||
# Workaround for https://github.com/lukesampson/scoop/issues/2220 until install is refactored
|
||||
# Workaround for https://github.com/ScoopInstaller/Scoop/issues/2220 until install is refactored
|
||||
# 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
|
||||
} else {
|
||||
$urls = url $manifest $architecture
|
||||
$urls = script:url $manifest $architecture
|
||||
|
||||
foreach ($url in $urls) {
|
||||
dl_with_cache $app $version $url $null $manifest.cookie $true
|
||||
@@ -220,7 +224,7 @@ function update($app, $global, $quiet = $false, $independent, $suggested, $use_c
|
||||
|
||||
if (!$ok) {
|
||||
error $err
|
||||
if (test-path $source) {
|
||||
if (Test-Path $source) {
|
||||
# rm cached file
|
||||
Remove-Item -force $source
|
||||
}
|
||||
@@ -237,20 +241,21 @@ function update($app, $global, $quiet = $false, $independent, $suggested, $use_c
|
||||
# endregion Workaround
|
||||
|
||||
$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
|
||||
$processdir = appdir $app $global | Resolve-Path | Select-Object -ExpandProperty Path
|
||||
if (Get-Process | Where-Object { $_.Path -like "$processdir\*" }) {
|
||||
error "Application is still running. Close all instances and try again."
|
||||
if (test_running_process $app $global) {
|
||||
return
|
||||
}
|
||||
#endregion Workaround for #2952
|
||||
|
||||
write-host "Uninstalling '$app' ($old_version)"
|
||||
Write-Host "Uninstalling '$app' ($old_version)"
|
||||
run_uninstaller $old_manifest $architecture $dir
|
||||
rm_shims $old_manifest $global $architecture
|
||||
env_rm_path $old_manifest $dir $global
|
||||
env_rm $old_manifest $global
|
||||
rm_shims $app $old_manifest $global $architecture
|
||||
env_rm_path $old_manifest $dir $global $architecture
|
||||
env_rm $old_manifest $global $architecture
|
||||
|
||||
# If a junction was used during install, that will have been used
|
||||
# as the reference directory. Otherwise it will just be the version
|
||||
@@ -269,6 +274,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"
|
||||
@@ -277,15 +284,25 @@ function update($app, $global, $quiet = $false, $independent, $suggested, $use_c
|
||||
# use the url of the install json if the application was installed through url
|
||||
$app = $install.url
|
||||
}
|
||||
install_app $app $architecture $global $suggested $use_cache $check_hash
|
||||
|
||||
if ($independent) {
|
||||
install_app $app $architecture $global $suggested $use_cache $check_hash
|
||||
} else {
|
||||
# Also add missing dependencies
|
||||
$apps = @(Get-Dependency $app $architecture) -ne $app
|
||||
ensure_none_failed $apps
|
||||
$apps.Where({ !(installed $_) }) + $app | ForEach-Object { install_app $_ $architecture $global $suggested $use_cache $check_hash }
|
||||
}
|
||||
}
|
||||
|
||||
if (!$apps) {
|
||||
if (-not ($apps -or $all)) {
|
||||
if ($global) {
|
||||
"scoop update: --global is invalid when <app> is not specified."; exit 1
|
||||
error 'scoop update: --global is invalid when <app> is not specified.'
|
||||
exit 1
|
||||
}
|
||||
if (!$use_cache) {
|
||||
"scoop update: --no-cache is invalid when <app> is not specified."; exit 1
|
||||
error 'scoop update: --no-cache is invalid when <app> is not specified.'
|
||||
exit 1
|
||||
}
|
||||
update_scoop
|
||||
} else {
|
||||
@@ -293,46 +310,57 @@ if (!$apps) {
|
||||
'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 ($apps_param -eq '*') {
|
||||
if ($updateScoop) {
|
||||
update_scoop
|
||||
}
|
||||
|
||||
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 {
|
||||
($app, $global) = $_
|
||||
$status = app_status $app $global
|
||||
if ($force -or $status.outdated) {
|
||||
if(!$status.hold) {
|
||||
if ($status.installed -and ($force -or $status.outdated)) {
|
||||
if (!$status.hold) {
|
||||
$outdated += applist $app $global
|
||||
write-host -f yellow ("$app`: $($status.version) -> $($status.latest_version){0}" -f ('',' (global)')[$global])
|
||||
Write-Host -f yellow ("$app`: $($status.version) -> $($status.latest_version){0}" -f ('', ' (global)')[$global])
|
||||
} else {
|
||||
warn "'$app' is locked to version $($status.version)"
|
||||
warn "'$app' is held to version $($status.version)"
|
||||
}
|
||||
} elseif ($apps_param -ne '*' -and !$all) {
|
||||
if ($status.installed) {
|
||||
ensure_none_failed $app
|
||||
Write-Host "$app`: $($status.version) (latest version)" -ForegroundColor Green
|
||||
} else {
|
||||
info 'Please reinstall it or fix the manifest.'
|
||||
}
|
||||
} elseif ($apps_param -ne '*') {
|
||||
write-host -f green "$app`: $($status.version) (latest version)"
|
||||
}
|
||||
}
|
||||
|
||||
if ($outdated -and (Test-Aria2Enabled)) {
|
||||
if ($outdated -and ((Test-Aria2Enabled) -and (get_config 'aria2-warning-enabled' $true))) {
|
||||
warn "Scoop uses 'aria2c' for multi-connection downloads."
|
||||
warn "Should it cause issues, run 'scoop config aria2-enabled false' to disable it."
|
||||
warn "To disable this warning, run 'scoop config aria2-warning-enabled false'."
|
||||
}
|
||||
if ($outdated.Length -gt 1) {
|
||||
write-host -f DarkCyan "Updating $($outdated.Length) outdated apps:"
|
||||
Write-Host -f DarkCyan "Updating $($outdated.Length) outdated apps:"
|
||||
} elseif ($outdated.Length -eq 0) {
|
||||
write-host -f Green "Latest versions for all apps are installed! For more information try 'scoop status'"
|
||||
Write-Host -f Green "Latest versions for all apps are installed! For more information try 'scoop status'"
|
||||
} else {
|
||||
write-host -f DarkCyan "Updating one outdated app:"
|
||||
Write-Host -f DarkCyan "Updating one outdated app:"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,77 +1,77 @@
|
||||
# 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
|
||||
# -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 allows
|
||||
# to avoid 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\getopt.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 'asnup' @('all', 'scan', 'no-depends', 'no-update-scoop', 'passthru')
|
||||
if ($err) { "scoop virustotal: $err"; exit 1 }
|
||||
if (!$apps) { my_usage; exit 1 }
|
||||
$architecture = ensure_architecture
|
||||
|
||||
$opt, $apps, $err = getopt $args 'a:sn' @('arch=', 'scan', 'no-depends')
|
||||
if($err) { "scoop virustotal: $err"; exit 1 }
|
||||
if(!$apps) { my_usage; exit 1 }
|
||||
$architecture = ensure_architecture ($opt.a + $opt.arch)
|
||||
|
||||
if(is_scoop_outdated) { scoop update }
|
||||
if (is_scoop_outdated) {
|
||||
if ($opt.u -or $opt.'no-update-scoop') {
|
||||
warn 'Scoop is out of date.'
|
||||
} else {
|
||||
scoop update
|
||||
}
|
||||
}
|
||||
|
||||
$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
|
||||
}
|
||||
|
||||
if (!$opt.n -and !$opt."no-depends") {
|
||||
$apps = install_order $apps $architecture
|
||||
if (!$opt.n -and !$opt.'no-depends') {
|
||||
$apps = $apps | Get-Dependency -Architecture $architecture | Select-Object -Unique
|
||||
}
|
||||
|
||||
$_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
|
||||
@@ -80,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
|
||||
@@ -152,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
|
||||
}
|
||||
|
||||
@@ -189,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)"
|
||||
@@ -200,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
|
||||
}
|
||||
|
||||
$urls = url $manifest $architecture
|
||||
[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,42 +2,19 @@
|
||||
# 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'; my_usage; exit 1 }
|
||||
|
||||
try {
|
||||
$gcm = Get-Command "$command" -ea stop
|
||||
} catch {
|
||||
abort "'$command' not found" 3
|
||||
if (!$command) {
|
||||
'ERROR: <command> missing'
|
||||
my_usage
|
||||
exit 1
|
||||
}
|
||||
|
||||
$path = "$($gcm.path)"
|
||||
$usershims = "$(resolve-path $(shimdir $false))"
|
||||
$globalshims = fullpath (shimdir $true) # don't resolve: may not exist
|
||||
$path = Get-CommandPath $command
|
||||
|
||||
if($path.endswith(".ps1") -and ($path -like "$usershims*" -or $path -like "$globalshims*")) {
|
||||
$shimtext = Get-Content $path
|
||||
|
||||
$exepath = ($shimtext | Where-Object { $_.startswith('$path') }).split(' ') | Select-Object -Last 1 | Invoke-Expression
|
||||
|
||||
if(![system.io.path]::ispathrooted($exepath)) {
|
||||
# Expand relative path
|
||||
$exepath = resolve-path (join-path (split-path $path) $exepath)
|
||||
}
|
||||
|
||||
friendly_path $exepath
|
||||
} elseif($gcm.commandtype -eq 'Application') {
|
||||
$gcm.Source
|
||||
} elseif($gcm.commandtype -eq 'Alias') {
|
||||
scoop which $gcm.resolvedcommandname
|
||||
} else {
|
||||
[console]::error.writeline("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
|
||||
|
||||
237
schema.json
237
schema.json
@@ -7,6 +7,10 @@
|
||||
"pattern": "^([a-fA-F0-9]{64}|(sha1|sha256|sha512|md5):([a-fA-F0-9]{32}|[a-fA-F0-9]{40}|[a-fA-F0-9]{64}|[a-fA-F0-9]{128}))$",
|
||||
"type": "string"
|
||||
},
|
||||
"jsonPathPattern": {
|
||||
"pattern": "^\\$[.\\[].*$",
|
||||
"type": "string"
|
||||
},
|
||||
"hash": {
|
||||
"anyOf": [
|
||||
{
|
||||
@@ -35,13 +39,11 @@
|
||||
"type": "string"
|
||||
},
|
||||
"jp": {
|
||||
"pattern": "^\\$[.[].*$",
|
||||
"type": "string",
|
||||
"$ref": "#/definitions/jsonPathPattern",
|
||||
"description": "Same as 'jsonpath'"
|
||||
},
|
||||
"jsonpath": {
|
||||
"pattern": "^\\$[.[].*$",
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/jsonPathPattern"
|
||||
},
|
||||
"xpath": {
|
||||
"type": "string"
|
||||
@@ -86,6 +88,21 @@
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"hashExtractionOrArrayOfHashExtractions": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/hashExtraction"
|
||||
},
|
||||
{
|
||||
"items": {
|
||||
"$ref": "#/definitions/hashExtraction"
|
||||
},
|
||||
"minItems": 1,
|
||||
"type": "array",
|
||||
"uniqueItems": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"architecture": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
@@ -117,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"
|
||||
},
|
||||
@@ -155,63 +178,93 @@
|
||||
"minItems": 1,
|
||||
"type": "array"
|
||||
},
|
||||
"autoupdate": {
|
||||
"autoupdateArch": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"architecture": {
|
||||
"url": {
|
||||
"$ref": "#/definitions/autoupdateUriOrArrayOfAutoupdateUris"
|
||||
},
|
||||
"hash": {
|
||||
"$ref": "#/definitions/hashExtractionOrArrayOfHashExtractions"
|
||||
},
|
||||
"extract_dir": {
|
||||
"$ref": "#/definitions/stringOrArrayOfStrings"
|
||||
},
|
||||
"extract_to": {
|
||||
"$ref": "#/definitions/stringOrArrayOfStrings"
|
||||
},
|
||||
"env_add_path": {
|
||||
"$ref": "#/definitions/stringOrArrayOfStrings"
|
||||
},
|
||||
"env_set": {
|
||||
"type": "object"
|
||||
},
|
||||
"bin": {
|
||||
"$ref": "#/definitions/stringOrArrayOfStringsOrAnArrayOfArrayOfStrings"
|
||||
},
|
||||
"shortcuts": {
|
||||
"$ref": "#/definitions/shortcutsArray"
|
||||
},
|
||||
"installer": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"32bit": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"extract_dir": {
|
||||
"type": "string"
|
||||
},
|
||||
"url": {
|
||||
"format": "uri",
|
||||
"type": "string"
|
||||
},
|
||||
"hash": {
|
||||
"$ref": "#/definitions/hashExtraction"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"64bit": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"extract_dir": {
|
||||
"type": "string"
|
||||
},
|
||||
"url": {
|
||||
"format": "uri",
|
||||
"type": "string"
|
||||
},
|
||||
"hash": {
|
||||
"$ref": "#/definitions/hashExtraction"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
"file": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"extract_dir": {
|
||||
"type": "string"
|
||||
},
|
||||
"hash": {
|
||||
"$ref": "#/definitions/hashExtraction"
|
||||
},
|
||||
"note": {
|
||||
"post_install": {
|
||||
"$ref": "#/definitions/stringOrArrayOfStrings"
|
||||
},
|
||||
"url": {
|
||||
"format": "uri",
|
||||
"type": "string"
|
||||
"psmodule": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"persist": {
|
||||
"$ref": "#/definitions/stringOrArrayOfStringsOrAnArrayOfArrayOfStrings"
|
||||
},
|
||||
"license": {
|
||||
"$ref": "#/definitions/license"
|
||||
},
|
||||
"notes": {
|
||||
"$ref": "#/definitions/stringOrArrayOfStrings"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"autoupdate": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/autoupdateArch"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"notes": {
|
||||
"$ref": "#/definitions/stringOrArrayOfStrings"
|
||||
},
|
||||
"architecture": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"32bit": {
|
||||
"$ref": "#/definitions/autoupdateArch"
|
||||
},
|
||||
"64bit": {
|
||||
"$ref": "#/definitions/autoupdateArch"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"checkver": {
|
||||
"anyOf": [
|
||||
{
|
||||
@@ -239,13 +292,11 @@
|
||||
"type": "string"
|
||||
},
|
||||
"jp": {
|
||||
"pattern": "^\\$[.[].*$",
|
||||
"type": "string",
|
||||
"$ref": "#/definitions/jsonPathPattern",
|
||||
"description": "Same as 'jsonpath'"
|
||||
},
|
||||
"jsonpath": {
|
||||
"pattern": "^\\$[.[].*$",
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/jsonPathPattern"
|
||||
},
|
||||
"xpath": {
|
||||
"type": "string"
|
||||
@@ -260,6 +311,10 @@
|
||||
},
|
||||
"useragent": {
|
||||
"type": "string"
|
||||
},
|
||||
"script": {
|
||||
"$ref": "#/definitions/stringOrArrayOfStrings",
|
||||
"description": "Custom PowerShell script to retrieve application version using more complex approach."
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
@@ -365,6 +420,23 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"autoupdateUriOrArrayOfAutoupdateUris": {
|
||||
"anyOf": [
|
||||
{
|
||||
"format": "uri",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"items": {
|
||||
"format": "uri",
|
||||
"type": "string"
|
||||
},
|
||||
"minItems": 1,
|
||||
"type": "array",
|
||||
"uniqueItems": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"licenseIdentifiers": {
|
||||
"type": "string",
|
||||
"description": "License identifier based on SPDX License List https://spdx.org/licenses/",
|
||||
@@ -390,9 +462,36 @@
|
||||
"Shareware",
|
||||
"Unlicense"
|
||||
]
|
||||
},
|
||||
"license": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/licenseIdentifiers"
|
||||
},
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"url": {
|
||||
"format": "uri",
|
||||
"type": "string"
|
||||
},
|
||||
"identifier": {
|
||||
"$ref": "#/definitions/licenseIdentifiers"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"identifier"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
"_comment": {
|
||||
"description": "Deprecated. Use ## instead.",
|
||||
"$ref": "#/definitions/stringOrArrayOfStrings"
|
||||
@@ -455,34 +554,14 @@
|
||||
"type": "string"
|
||||
},
|
||||
"innosetup": {
|
||||
"description": "True if the installer InnoSetup based. Found in https://github.com/lukesampson/scoop/search?l=JSON&q=innosetup",
|
||||
"description": "True if the installer InnoSetup based. Found in https://github.com/ScoopInstaller/Main/search?l=JSON&q=innosetup",
|
||||
"type": "boolean"
|
||||
},
|
||||
"installer": {
|
||||
"$ref": "#/definitions/installer"
|
||||
},
|
||||
"license": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/licenseIdentifiers"
|
||||
},
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"url": {
|
||||
"format": "uri",
|
||||
"type": "string"
|
||||
},
|
||||
"identifier": {
|
||||
"$ref": "#/definitions/licenseIdentifiers"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"identifier"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
"$ref": "#/definitions/license"
|
||||
},
|
||||
"msi": {
|
||||
"$ref": "#/definitions/stringOrArrayOfStrings",
|
||||
@@ -494,9 +573,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": {
|
||||
@@ -525,12 +610,14 @@
|
||||
"$ref": "#/definitions/uriOrArrayOfUris"
|
||||
},
|
||||
"version": {
|
||||
"pattern": "^[\\w\\.\\-_]+$",
|
||||
"pattern": "^[\\w\\.\\-+_]+$",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"version"
|
||||
"version",
|
||||
"homepage",
|
||||
"license"
|
||||
],
|
||||
"title": "scoop app manifest schema",
|
||||
"type": "object"
|
||||
|
||||
93
supporting/formats/ScoopTypes.Format.ps1xml
Normal file
93
supporting/formats/ScoopTypes.Format.ps1xml
Normal file
@@ -0,0 +1,93 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Configuration>
|
||||
<ViewDefinitions>
|
||||
<View>
|
||||
<Name>ScoopAppsType</Name>
|
||||
<ViewSelectedBy>
|
||||
<TypeName>ScoopApps</TypeName>
|
||||
</ViewSelectedBy>
|
||||
<TableControl>
|
||||
<TableRowEntries>
|
||||
<TableRowEntry>
|
||||
<TableColumnItems>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Name</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Version</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Source</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Updated</PropertyName>
|
||||
<FormatString>yyyy-MM-dd HH:mm:ss</FormatString>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Info</PropertyName>
|
||||
</TableColumnItem>
|
||||
</TableColumnItems>
|
||||
</TableRowEntry>
|
||||
</TableRowEntries>
|
||||
</TableControl>
|
||||
</View>
|
||||
<View>
|
||||
<Name>ScoopShimsType</Name>
|
||||
<ViewSelectedBy>
|
||||
<TypeName>ScoopShims</TypeName>
|
||||
</ViewSelectedBy>
|
||||
<TableControl>
|
||||
<TableRowEntries>
|
||||
<TableRowEntry>
|
||||
<TableColumnItems>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Name</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Source</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Alternatives</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>IsGlobal</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>IsHidden</PropertyName>
|
||||
</TableColumnItem>
|
||||
</TableColumnItems>
|
||||
</TableRowEntry>
|
||||
</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 +1 @@
|
||||
cb440b8a08a2095a59666a859b35aa5a1524b140b909ecc760f38f3baccf80e6 *shim.exe
|
||||
9726c3a429009a5b22bd92cb8ab96724c670e164e7240e83f27b7c8b7bd1ca39 *shim.exe
|
||||
|
||||
@@ -1 +1 @@
|
||||
710aeef5381f96ea0360a27ce6b792f67e018abb91d6dc67fc5c18c15baf611f36268a3f9e70a339b1a1b0e5dbfdaee10d74288352e609764d5b81303409a332 *shim.exe
|
||||
18a737674afde4d5e7e1647d8d1e98471bb260513c57739651f92fdf1647d76c92f0cd0a9bb458daf4eae4bdab9d31404162acf6d74a041e6415752b75d722e0 *shim.exe
|
||||
|
||||
Binary file not shown.
@@ -1,21 +1,22 @@
|
||||
Param([Switch]$Fast)
|
||||
Push-Location $psscriptroot
|
||||
. "$psscriptroot\..\..\lib\install.ps1"
|
||||
Push-Location $PSScriptRoot
|
||||
. "$PSScriptRoot\..\..\lib\core.ps1"
|
||||
. "$PSScriptRoot\..\..\lib\install.ps1"
|
||||
|
||||
if(!$Fast) {
|
||||
if (!$Fast) {
|
||||
Write-Host "Install dependencies ..."
|
||||
Invoke-Expression "$psscriptroot\install.ps1"
|
||||
& "$PSScriptRoot\install.ps1"
|
||||
}
|
||||
|
||||
$output = "$psscriptroot\bin"
|
||||
$output = "$PSScriptRoot\bin"
|
||||
Write-Output 'Compiling shim.cs ...'
|
||||
& "$psscriptroot\packages\Microsoft.Net.Compilers\tools\csc.exe" /deterministic /platform:anycpu /nologo /optimize /target:exe /out:"$output\shim.exe" shim.cs
|
||||
& "$PSScriptRoot\packages\Microsoft.Net.Compilers.Toolset\tasks\net472\csc.exe" -deterministic -platform:anycpu -nologo -optimize -target:exe -out:"$output\shim.exe" shim.cs
|
||||
|
||||
Write-Output 'Computing checksums ...'
|
||||
Remove-Item "$psscriptroot\bin\checksum.sha256" -ErrorAction Ignore
|
||||
Remove-Item "$psscriptroot\bin\checksum.sha512" -ErrorAction Ignore
|
||||
Get-ChildItem "$psscriptroot\bin\*" -Include *.exe,*.dll | ForEach-Object {
|
||||
"$(compute_hash $_ 'sha256') *$($_.Name)" | Out-File "$psscriptroot\bin\checksum.sha256" -Append -Encoding oem
|
||||
"$(compute_hash $_ 'sha512') *$($_.Name)" | Out-File "$psscriptroot\bin\checksum.sha512" -Append -Encoding oem
|
||||
Remove-Item "$PSScriptRoot\bin\checksum.sha256" -ErrorAction Ignore
|
||||
Remove-Item "$PSScriptRoot\bin\checksum.sha512" -ErrorAction Ignore
|
||||
Get-ChildItem "$PSScriptRoot\bin\*" -Include *.exe, *.dll | ForEach-Object {
|
||||
"$(compute_hash $_ 'sha256') *$($_.Name)" | Out-File "$PSScriptRoot\bin\checksum.sha256" -Append -Encoding oem
|
||||
"$(compute_hash $_ 'sha512') *$($_.Name)" | Out-File "$PSScriptRoot\bin\checksum.sha512" -Append -Encoding oem
|
||||
}
|
||||
Pop-Location
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# https://github.com/edymtt/nugetstandalone
|
||||
$destinationFolder = "$psscriptroot\packages"
|
||||
if ((Test-Path -path $destinationFolder)) {
|
||||
$destinationFolder = "$PSScriptRoot\packages"
|
||||
if ((Test-Path -Path $destinationFolder)) {
|
||||
Remove-Item -Path $destinationFolder -Recurse | Out-Null
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Net.Compilers" version="2.10.0" 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.2.10.0\build\Microsoft.Net.Compilers.props" Condition="Exists('packages\Microsoft.Net.Compilers.2.10.0\build\Microsoft.Net.Compilers.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.2.10.0\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.Net.Compilers.2.10.0\build\Microsoft.Net.Compilers.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 @@
|
||||
# https://github.com/edymtt/nugetstandalone
|
||||
$destinationFolder = "$psscriptroot\packages"
|
||||
if (!(Test-Path -path $destinationFolder)) {
|
||||
$destinationFolder = "$PSScriptRoot\packages"
|
||||
if (!(Test-Path -Path $destinationFolder)) {
|
||||
Write-Host -f Red "Run .\install.ps1 first!"
|
||||
exit 1
|
||||
}
|
||||
|
||||
1
supporting/shims/71/checksum.sha256
Normal file
1
supporting/shims/71/checksum.sha256
Normal file
@@ -0,0 +1 @@
|
||||
70d4690b8ac3b3f715f537cdea6e07a39fda4bc0347bf6b958e4f3ff2f0e04d4 shim.exe
|
||||
1
supporting/shims/71/checksum.sha512
Normal file
1
supporting/shims/71/checksum.sha512
Normal file
@@ -0,0 +1 @@
|
||||
ecde07b32192846c4885cf4d2208eedc170765ea115ae49b81509fed0ce474e21064100bb2f3d815ee79f1c12463d32ef013d4182647eae71855cd18e4196176 shim.exe
|
||||
BIN
supporting/shims/71/shim.exe
Normal file
BIN
supporting/shims/71/shim.exe
Normal file
Binary file not shown.
2
supporting/shims/kiennq/.gitignore
vendored
Normal file
2
supporting/shims/kiennq/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*.zip
|
||||
*.bak
|
||||
52
supporting/shims/kiennq/Makefile
Normal file
52
supporting/shims/kiennq/Makefile
Normal file
@@ -0,0 +1,52 @@
|
||||
|
||||
VER?=2.2.1
|
||||
ZIP=shimexe.zip
|
||||
URL?=https://github.com/kiennq/scoop-better-shimexe/releases/download/$(VER)/$(ZIP)
|
||||
LATEST_URL?=https://github.com/kiennq/scoop-better-shimexe/releases/latest
|
||||
NEWVER=$(shell cat version.txt)
|
||||
|
||||
all: verify ## make download unzip verify
|
||||
|
||||
version.txt:
|
||||
@curl --max-redirs 0 -s -D - -o /dev/null $(LATEST_URL) | grep -i ^location | sed -E -e "s|.*/([^/]+)$$|\1|" >version.txt
|
||||
@printf "%s " "Latest version is:"
|
||||
@cat version.txt
|
||||
|
||||
check: version.txt ## Check the version number in version.txt and update if needed
|
||||
|
||||
bump: check ## Bump version number in Makefile
|
||||
@rm -f Makefile.bak
|
||||
@sed -i.bak -e 's|=$(VER)|=$(NEWVER)|' Makefile
|
||||
@cmp --quiet Makefile{,.bak} || echo "Makefile bumped from $(VER) to $(NEWVER)"
|
||||
|
||||
$(ZIP): version.txt
|
||||
curl -L -s -o $(ZIP) $(URL)
|
||||
@touch $@
|
||||
|
||||
download: $(ZIP) ## Download shim from https://github.com/kiennq/scoop-better-shimexe
|
||||
|
||||
shim.exe: $(ZIP)
|
||||
unzip -z -j -o $(ZIP)
|
||||
@touch $@
|
||||
|
||||
unzip: shim.exe ## Unzip download
|
||||
|
||||
verify: shim.exe ## Verify SHA256 checksum for shim.exe
|
||||
sed -e "s|bin/||" checksum.sha256 | sha256sum -c
|
||||
|
||||
clean: ## Clean .zip files
|
||||
rm -f *.zip
|
||||
|
||||
help: ## Display help text
|
||||
@printf "%-8s %s\n" Target Description
|
||||
@printf "%-8s %s\n" '--------' '------------------------------------------'
|
||||
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "%-8s %s\n", $$1, $$2}'
|
||||
|
||||
.PHONY: all
|
||||
.PHONY: bump
|
||||
.PHONY: check
|
||||
.PHONY: clean
|
||||
.PHONY: download
|
||||
.PHONY: help
|
||||
.PHONY: unzip
|
||||
.PHONY: verify
|
||||
1
supporting/shims/kiennq/checksum.sha256
Normal file
1
supporting/shims/kiennq/checksum.sha256
Normal file
@@ -0,0 +1 @@
|
||||
aa685053f4a5c0e7145f2a27514c8a56ceae25b0824062326f04037937caa558 bin/shim.exe
|
||||
1
supporting/shims/kiennq/checksum.sha512
Normal file
1
supporting/shims/kiennq/checksum.sha512
Normal file
@@ -0,0 +1 @@
|
||||
67c605c8163869d8ef8153c64eb09b82645cbae8228928c0fef944d0259a7b2d3791ecf4b4b01e23566916a878ee7977bfc1a59846bccf3c63bd6a1cf4f521b5 bin/shim.exe
|
||||
BIN
supporting/shims/kiennq/shim.exe
Normal file
BIN
supporting/shims/kiennq/shim.exe
Normal file
Binary file not shown.
1
supporting/shims/kiennq/version.txt
Normal file
1
supporting/shims/kiennq/version.txt
Normal file
@@ -0,0 +1 @@
|
||||
2.2.1
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,4 @@
|
||||
f58c374ffcaae4e36d740d90fbf7fe70d0abb7328cd9af3a0a7b70803e994ba4 *Newtonsoft.Json.dll
|
||||
4ca0681df3755205cfd178aaa38f7fd6818c183c4d14c77cd77f8d777282a650 *Newtonsoft.Json.Schema.dll
|
||||
55f1292b42fa2e8a7a4b3eb360fcfffe97be0fe043b4709979fcd37b8a400456 *Scoop.Validator.dll
|
||||
6dfbe59d9f1c17d362bdd4509975ab40d96e09585aa8d0a1f5527dafbc1c3727 *validator.exe
|
||||
b624949df8b0e3a6153fdfb730a7c6f4990b6592ee0d922e1788433d276610f3 *Newtonsoft.Json.dll
|
||||
9abb57d73d82a2d77008321a85aff2b62e5ac68bebb54ece8668c96cc112e36b *Newtonsoft.Json.Schema.dll
|
||||
0318c8221ce4d44806f8def619bcc02886be0902aab80080e6251c50c6ca53a9 *Scoop.Validator.dll
|
||||
40a70bee96d108701f8f2e81392f9b79fd003f1cb4e1653ad2429753153fd7ee *validator.exe
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
7cbbbef742f56af80f1012d7da86fe5375ac05813045756fb45d0691c36ef13c069361457500ba4200157d5ee7922fd118bf4c0635e5192e3f8c6183fd580944 *Newtonsoft.Json.dll
|
||||
2d9c630948c21b325af7b7ad3de9219a3fdcc23fa65270a0cdb78ea9609038434f6b741a968e9758f93782a3bab57efc3072429bdf84f8ad25a94bf5a8c4bc48 *Newtonsoft.Json.Schema.dll
|
||||
6d89196ce01823093f5c3da98d34d41bc1733316d04e181d6f24982a373cc28487b8e4e74b54f5a665a550ad4a7fa6d7208d99de947b82b20e524747bdb41bdd *Scoop.Validator.dll
|
||||
e9b2162b3e109291c61005a9088d9ebf30fb0307fa285ccf5c54aef05fb3c02944d9513f2557e75d670682b0f3ba67a4f8df3561c715eaa5daa077b591fb4d54 *validator.exe
|
||||
2fdf035661f349206f58ea1feed8805b7f9517a21f9c113e7301c69de160f184c774350a12a710046e3ff6baa37345d319b6f47fd24fbba4e042d54014bee511 *Newtonsoft.Json.dll
|
||||
855ab2e30c9d523c9f321ae861c5969244185f660fa47e05cec96df8e2970d19843dbd3d89a0fca845544641915d1adf4b4a2145ef568dd99da7791e5064d70e *Newtonsoft.Json.Schema.dll
|
||||
338793e6127330c0b05728291fcf18441127ffb56e1bd5c0f0588cd7436605f4b852f4bb622f655896a7eb7b1262add142b200fd5f37391b47d1401becb6b81c *Scoop.Validator.dll
|
||||
d497c27b48f44f4cff270d3c8801b0cecc74108f8786a4a7c40e57541308ae33a69f5456cfc43ae1ce4214038d20da9fbeac1bcf76cc58d972863b58dab18401 *validator.exe
|
||||
|
||||
Binary file not shown.
@@ -1,26 +1,27 @@
|
||||
Param([Switch]$Fast)
|
||||
Push-Location $psscriptroot
|
||||
. "$psscriptroot\..\..\lib\install.ps1"
|
||||
Push-Location $PSScriptRoot
|
||||
. "$PSScriptRoot\..\..\lib\core.ps1"
|
||||
. "$PSScriptRoot\..\..\lib\install.ps1"
|
||||
|
||||
if(!$Fast) {
|
||||
if (!$Fast) {
|
||||
Write-Host "Install dependencies ..."
|
||||
Invoke-Expression "$psscriptroot\install.ps1"
|
||||
& "$PSScriptRoot\install.ps1"
|
||||
}
|
||||
|
||||
$output = "$psscriptroot\bin"
|
||||
if(!$Fast) {
|
||||
Get-ChildItem "$psscriptroot\packages\Newtonsoft.*\lib\net45\*.dll" -File | ForEach-Object { Copy-Item $_ $output }
|
||||
$output = "$PSScriptRoot\bin"
|
||||
if (!$Fast) {
|
||||
Get-ChildItem "$PSScriptRoot\packages\Newtonsoft.*\lib\net45\*.dll" -File | ForEach-Object { Copy-Item $_ $output }
|
||||
}
|
||||
Write-Output 'Compiling Scoop.Validator.cs ...'
|
||||
& "$psscriptroot\packages\Microsoft.Net.Compilers\tools\csc.exe" /deterministic /platform:anycpu /nologo /optimize /target:library /reference:"$output\Newtonsoft.Json.dll","$output\Newtonsoft.Json.Schema.dll" /out:"$output\Scoop.Validator.dll" Scoop.Validator.cs
|
||||
& "$PSScriptRoot\packages\Microsoft.Net.Compilers.Toolset\tasks\net472\csc.exe" -deterministic -platform:anycpu -nologo -optimize -target:library -reference:"$output\Newtonsoft.Json.dll" -reference:"$output\Newtonsoft.Json.Schema.dll" -out:"$output\Scoop.Validator.dll" Scoop.Validator.cs
|
||||
Write-Output 'Compiling validator.cs ...'
|
||||
& "$psscriptroot\packages\Microsoft.Net.Compilers\tools\csc.exe" /deterministic /platform:anycpu /nologo /optimize /target:exe /reference:"$output\Scoop.Validator.dll","$output\Newtonsoft.Json.dll","$output\Newtonsoft.Json.Schema.dll" /out:"$output\validator.exe" validator.cs
|
||||
& "$PSScriptRoot\packages\Microsoft.Net.Compilers.Toolset\tasks\net472\csc.exe" -deterministic -platform:anycpu -nologo -optimize -target:exe -reference:"$output\Scoop.Validator.dll" -reference:"$output\Newtonsoft.Json.dll" -reference:"$output\Newtonsoft.Json.Schema.dll" -out:"$output\validator.exe" validator.cs
|
||||
|
||||
Write-Output 'Computing checksums ...'
|
||||
Remove-Item "$psscriptroot\bin\checksum.sha256" -ErrorAction Ignore
|
||||
Remove-Item "$psscriptroot\bin\checksum.sha512" -ErrorAction Ignore
|
||||
Get-ChildItem "$psscriptroot\bin\*" -Include *.exe,*.dll | ForEach-Object {
|
||||
"$(compute_hash $_ 'sha256') *$($_.Name)" | Out-File "$psscriptroot\bin\checksum.sha256" -Append -Encoding oem
|
||||
"$(compute_hash $_ 'sha512') *$($_.Name)" | Out-File "$psscriptroot\bin\checksum.sha512" -Append -Encoding oem
|
||||
Remove-Item "$PSScriptRoot\bin\checksum.sha256" -ErrorAction Ignore
|
||||
Remove-Item "$PSScriptRoot\bin\checksum.sha512" -ErrorAction Ignore
|
||||
Get-ChildItem "$PSScriptRoot\bin\*" -Include *.exe, *.dll | ForEach-Object {
|
||||
"$(compute_hash $_ 'sha256') *$($_.Name)" | Out-File "$PSScriptRoot\bin\checksum.sha256" -Append -Encoding oem
|
||||
"$(compute_hash $_ 'sha512') *$($_.Name)" | Out-File "$PSScriptRoot\bin\checksum.sha512" -Append -Encoding oem
|
||||
}
|
||||
Pop-Location
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# https://github.com/edymtt/nugetstandalone
|
||||
$destinationFolder = "$psscriptroot\packages"
|
||||
if ((Test-Path -path $destinationFolder)) {
|
||||
$destinationFolder = "$PSScriptRoot\packages"
|
||||
if ((Test-Path -Path $destinationFolder)) {
|
||||
Remove-Item -Path $destinationFolder -Recurse | Out-Null
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Newtonsoft.Json" version="11.0.2" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json.Schema" version="3.0.10" targetFramework="net45" />
|
||||
<package id="Microsoft.Net.Compilers" version="2.10.0" 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,6 +1,6 @@
|
||||
# https://github.com/edymtt/nugetstandalone
|
||||
$destinationFolder = "$psscriptroot\packages"
|
||||
if (!(Test-Path -path $destinationFolder)) {
|
||||
$destinationFolder = "$PSScriptRoot\packages"
|
||||
if (!(Test-Path -Path $destinationFolder)) {
|
||||
Write-Host -f Red "Run .\install.ps1 first!"
|
||||
exit 1
|
||||
}
|
||||
|
||||
@@ -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.2.10.0\build\Microsoft.Net.Compilers.props" Condition="Exists('packages\Microsoft.Net.Compilers.2.10.0\build\Microsoft.Net.Compilers.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=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
|
||||
<HintPath>packages\Newtonsoft.Json.11.0.2\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.10\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.2.10.0\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.Net.Compilers.2.10.0\build\Microsoft.Net.Compilers.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,29 +1,28 @@
|
||||
$repo_dir = (Get-Item $MyInvocation.MyCommand.Path).Directory.Parent.FullName
|
||||
|
||||
$repo_files = @( Get-ChildItem $repo_dir -file -recurse -force )
|
||||
$repo_files = @( Get-ChildItem $repo_dir -File -Recurse -Force )
|
||||
|
||||
$project_file_exclusions = @(
|
||||
$([regex]::Escape($repo_dir)+'(\\|/).git(\\|/).*$'),
|
||||
'.sublime-workspace$',
|
||||
'.DS_Store$',
|
||||
'[\\/]\.git[\\/]',
|
||||
'\.sublime-workspace$',
|
||||
'\.DS_Store$',
|
||||
'supporting(\\|/)validator(\\|/)packages(\\|/)*',
|
||||
'supporting(\\|/)shimexe(\\|/)packages(\\|/)*'
|
||||
)
|
||||
|
||||
describe 'Project code' {
|
||||
Describe 'Project code' {
|
||||
|
||||
$files = @(
|
||||
$repo_files |
|
||||
where-object { $_.fullname -inotmatch $($project_file_exclusions -join '|') } |
|
||||
where-object { $_.fullname -imatch '.(ps1|psm1)$' }
|
||||
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"
|
||||
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'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,30 +48,27 @@ describe 'Project code' {
|
||||
$null = [System.Management.Automation.PSParser]::Tokenize($contents, [ref]$errors)
|
||||
|
||||
New-Object psobject -Property @{
|
||||
Path = $scriptPath
|
||||
Path = $scriptPath
|
||||
SyntaxErrorsFound = ($errors.Count -gt 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it 'PowerShell code files do not contain syntax errors' -skip:$(-not $files_exist) {
|
||||
It 'PowerShell code files do not contain syntax errors' -Skip:$(-not $files_exist) {
|
||||
$badFiles = @(
|
||||
foreach ($file in $files)
|
||||
{
|
||||
if ( (Test-PowerShellSyntax $file.FullName).SyntaxErrorsFound )
|
||||
{
|
||||
foreach ($file in $files) {
|
||||
if ( (Test-PowerShellSyntax $file.FullName).SyntaxErrorsFound ) {
|
||||
$file.FullName
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
if ($badFiles.Count -gt 0)
|
||||
{
|
||||
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"
|
||||
. "$PSScriptRoot\Import-File-Tests.ps1"
|
||||
|
||||
@@ -1,28 +1,127 @@
|
||||
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
|
||||
}
|
||||
|
||||
. "$psscriptroot\Scoop-TestLib.ps1"
|
||||
. "$psscriptroot\..\lib\core.ps1"
|
||||
. "$psscriptroot\..\lib\manifest.ps1"
|
||||
. "$psscriptroot\..\lib\unix.ps1"
|
||||
|
||||
$repo_dir = (Get-Item $MyInvocation.PSScriptRoot).FullName
|
||||
|
||||
$repo_files = @(Get-ChildItem $repo_dir -file -recurse)
|
||||
|
||||
$project_file_exclusions = @(
|
||||
$([regex]::Escape($repo_dir)+'(\\|/).git(\\|/).*$'),
|
||||
'.sublime-workspace$',
|
||||
'.DS_Store$',
|
||||
'supporting(\\|/)validator(\\|/)packages(\\|/)*'
|
||||
#Requires -Version 5.0
|
||||
#Requires -Modules @{ ModuleName = 'Pester'; RequiredVersion = '4.10.1' }
|
||||
param(
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[String]
|
||||
$repo_dir = (Split-Path -Path $MyInvocation.PSCommandPath -Parent)
|
||||
)
|
||||
|
||||
. "$PSScriptRoot\Scoop-TestLib.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1"
|
||||
. "$PSScriptRoot\..\lib\unix.ps1"
|
||||
|
||||
$bucketdir = $repo_dir
|
||||
if(Test-Path("$repo_dir\bucket")) {
|
||||
if (Test-Path("$repo_dir\..\bucket")) {
|
||||
$bucketdir = "$repo_dir\..\bucket"
|
||||
} elseif (Test-Path("$repo_dir\bucket")) {
|
||||
$bucketdir = "$repo_dir\bucket"
|
||||
}
|
||||
|
||||
. "$psscriptroot\Import-File-Tests.ps1"
|
||||
. "$psscriptroot\Scoop-Manifest.Tests.ps1" -bucketdir $bucketdir
|
||||
# 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
|
||||
}
|
||||
}
|
||||
|
||||
Context 'schema validation' {
|
||||
It 'fails with broken schema' {
|
||||
$validator = New-Object Scoop.Validator("$working_dir\broken_schema.json", $true)
|
||||
$validator.Validate("$working_dir\wget.json") | Should -BeFalse
|
||||
$validator.Errors.Count | Should -Be 1
|
||||
$validator.Errors | Select-Object -First 1 | Should -Match 'broken_schema.*(line 6).*(position 4)'
|
||||
}
|
||||
It 'fails with broken manifest' {
|
||||
$validator = New-Object Scoop.Validator($schema, $true)
|
||||
$validator.Validate("$working_dir\broken_wget.json") | Should -BeFalse
|
||||
$validator.Errors.Count | Should -Be 1
|
||||
$validator.Errors | Select-Object -First 1 | Should -Match 'broken_wget.*(line 5).*(position 4)'
|
||||
}
|
||||
It 'fails with invalid manifest' {
|
||||
$validator = New-Object Scoop.Validator($schema, $true)
|
||||
$validator.Validate("$working_dir\invalid_wget.json") | Should -BeFalse
|
||||
$validator.Errors.Count | Should -Be 16
|
||||
$validator.Errors | Select-Object -First 1 | Should -Match "Property 'randomproperty' has not been defined and the schema does not allow additional properties\."
|
||||
$validator.Errors | Select-Object -Last 1 | Should -Match 'Required properties are missing from object: version\.'
|
||||
}
|
||||
}
|
||||
}
|
||||
Describe 'manifest validates against the schema' -Tag 'Manifests' {
|
||||
BeforeAll {
|
||||
$schema = "$PSScriptRoot\..\schema.json"
|
||||
$changed_manifests = @()
|
||||
if ($env:CI -eq $true) {
|
||||
# AppVeyor
|
||||
$commit = if ($env:APPVEYOR_PULL_REQUEST_HEAD_COMMIT) { $env:APPVEYOR_PULL_REQUEST_HEAD_COMMIT } else { $env:APPVEYOR_REPO_COMMIT }
|
||||
|
||||
# GitHub Actions
|
||||
if ($env:GITHUB_SHA) {
|
||||
$commit = $env:GITHUB_SHA
|
||||
}
|
||||
$changed_manifests = (Get-GitChangedFile -Path $repo_dir -Include '*.json' -Commit $commit)
|
||||
}
|
||||
$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 {
|
||||
$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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$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,35 +1,33 @@
|
||||
if([String]::IsNullOrEmpty($MyInvocation.PSScriptRoot)) {
|
||||
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' {
|
||||
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)' }
|
||||
Where-Object { $_.fullname -inotmatch $($project_file_exclusions -join '|') } |
|
||||
Where-Object { $_.fullname -inotmatch '(.exe|.zip|.dll)$' } |
|
||||
Where-Object { $_.fullname -inotmatch '(unformatted)' }
|
||||
)
|
||||
|
||||
$files_exist = ($files.Count -gt 0)
|
||||
|
||||
it $('non-binary project files exist ({0} found)' -f $files.Count) -skip:$(-not $files_exist) {
|
||||
if (-not ($files.Count -gt 0))
|
||||
{
|
||||
throw "No non-binary project were found"
|
||||
It $('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) {
|
||||
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')) {
|
||||
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 {
|
||||
@@ -41,49 +39,42 @@ describe 'Style constraints for non-binary project files' {
|
||||
}
|
||||
)
|
||||
|
||||
if ($badFiles.Count -gt 0)
|
||||
{
|
||||
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) {
|
||||
It 'files end with a newline' -Skip:$(-not $files_exist) {
|
||||
$badFiles = @(
|
||||
foreach ($file in $files)
|
||||
{
|
||||
foreach ($file in $files) {
|
||||
# Ignore previous TestResults.xml
|
||||
if ($file -match "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")
|
||||
{
|
||||
if ($string.Length -gt 0 -and $string[-1] -ne "`n") {
|
||||
$file.FullName
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
if ($badFiles.Count -gt 0)
|
||||
{
|
||||
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) {
|
||||
It 'file newlines are CRLF' -Skip:$(-not $files_exist) {
|
||||
$badFiles = @(
|
||||
foreach ($file in $files)
|
||||
{
|
||||
$content = Get-Content -raw $file.FullName
|
||||
if(!$content) {
|
||||
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 )
|
||||
{
|
||||
for ($i = 0; $i -lt $lineCount; $i++) {
|
||||
if ( [regex]::match($lines[$i], '\r|\n').success ) {
|
||||
$file.FullName
|
||||
break
|
||||
}
|
||||
@@ -91,52 +82,43 @@ describe 'Style constraints for non-binary project files' {
|
||||
}
|
||||
)
|
||||
|
||||
if ($badFiles.Count -gt 0)
|
||||
{
|
||||
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) {
|
||||
It 'files have no lines containing trailing whitespace' -Skip:$(-not $files_exist) {
|
||||
$badLines = @(
|
||||
foreach ($file in $files)
|
||||
{
|
||||
foreach ($file in $files) {
|
||||
# Ignore previous TestResults.xml
|
||||
if ($file -match "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+$')
|
||||
{
|
||||
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)
|
||||
{
|
||||
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) {
|
||||
It 'any leading whitespace consists only of spaces (excepting makefiles)' -Skip:$(-not $files_exist) {
|
||||
$badLines = @(
|
||||
foreach ($file in $files)
|
||||
{
|
||||
if ($file.fullname -inotmatch '(^|.)makefile$')
|
||||
{
|
||||
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|$)')
|
||||
{
|
||||
for ($i = 0; $i -lt $lineCount; $i++) {
|
||||
if ($lines[$i] -notmatch '^[ ]*(\S|$)') {
|
||||
'File: {0}, Line: {1}' -f $file.FullName, ($i + 1)
|
||||
}
|
||||
}
|
||||
@@ -144,8 +126,7 @@ describe 'Style constraints for non-binary project files' {
|
||||
}
|
||||
)
|
||||
|
||||
if ($badLines.Count -gt 0)
|
||||
{
|
||||
if ($badLines.Count -gt 0) {
|
||||
throw "The following $($badLines.Count) lines contain TABs within leading whitespace: `r`n`r`n$($badLines -join "`r`n")"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,55 +1,56 @@
|
||||
. "$psscriptroot\..\libexec\scoop-alias.ps1" | out-null
|
||||
. "$PSScriptRoot\Scoop-TestLib.ps1"
|
||||
. "$PSScriptRoot\..\lib\core.ps1"
|
||||
. "$PSScriptRoot\..\lib\help.ps1"
|
||||
. "$PSScriptRoot\..\libexec\scoop-alias.ps1" | Out-Null
|
||||
|
||||
reset_aliases
|
||||
Describe 'add_alias' -Tag 'Scoop' {
|
||||
Mock shimdir { "$env:TEMP\shims" }
|
||||
Mock set_config { }
|
||||
Mock get_config { @{} }
|
||||
|
||||
describe "add_alias" -Tag 'Scoop' {
|
||||
mock shimdir { "TestDrive:\shim" }
|
||||
mock set_config { }
|
||||
mock get_config { @{} }
|
||||
$shimdir = shimdir
|
||||
ensure $shimdir
|
||||
|
||||
$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
|
||||
|
||||
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!"
|
||||
add_alias 'rm' '"hello, world!"'
|
||||
& $alias_file | Should -Be 'hello, world!'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context "alias exists" {
|
||||
it "does not change existing alias" {
|
||||
$alias_file = "$shimdir\scoop-rm.ps1"
|
||||
new-item $alias_file -type file
|
||||
$alias_file | should -exist
|
||||
Context 'alias exists' {
|
||||
It 'does not change existing alias' {
|
||||
$alias_file = "$shimdir\scoop-rm.ps1"
|
||||
New-Item $alias_file -Type File -Force
|
||||
$alias_file | Should -Exist
|
||||
|
||||
add_alias "rm" "test"
|
||||
$alias_file | should -FileContentMatch ""
|
||||
add_alias 'rm' 'test'
|
||||
$alias_file | Should -FileContentMatch ''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
describe "rm_alias" {
|
||||
mock shimdir { "TestDrive:\shim" }
|
||||
mock set_config { }
|
||||
mock get_config { @{} }
|
||||
Describe 'rm_alias' -Tag 'Scoop' {
|
||||
Mock shimdir { "$env:TEMP\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!"'
|
||||
Context 'alias exists' {
|
||||
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"}) }
|
||||
$alias_file | Should -Exist
|
||||
Mock get_config { @(@{'rm' = 'scoop-rm' }) }
|
||||
|
||||
rm_alias "rm"
|
||||
$alias_file | should -not -exist
|
||||
rm_alias 'rm'
|
||||
$alias_file | Should -Not -Exist
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user