mirror of
				https://github.com/ScoopInstaller/Scoop.git
				synced 2025-10-30 14:17:54 +00:00 
			
		
		
		
	Compare commits
	
		
			240 Commits
		
	
	
		
			2019-10-18
			...
			v0.2.0
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 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, | ||||
|   | ||||
							
								
								
									
										565
									
								
								CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										565
									
								
								CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,565 @@ | ||||
| ## [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="https://github.com/ScoopInstaller/Scoop/blob/master/LICENSE"> | ||||
|         <img src="https://img.shields.io/github/license/ScoopInstaller/Scoop.svg" 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/TheRandomLabs/scoop-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,6 +41,7 @@ param( | ||||
|         $true | ||||
|     })] | ||||
|     [String] $Upstream, | ||||
|     [String] $OriginBranch = 'master', | ||||
|     [String] $App = '*', | ||||
|     [Parameter(Mandatory = $true)] | ||||
|     [ValidateScript( { | ||||
| @@ -51,7 +56,8 @@ param( | ||||
|     [Switch] $Request, | ||||
|     [Switch] $Help, | ||||
|     [string[]] $SpecialSnowflakes, | ||||
|     [Switch] $SkipUpdated | ||||
|     [Switch] $SkipUpdated, | ||||
|     [Switch] $ThrowError | ||||
| ) | ||||
|  | ||||
| . "$PSScriptRoot\..\lib\manifest.ps1" | ||||
| @@ -65,12 +71,12 @@ if ((!$Push -and !$Request) -or $Help) { | ||||
| 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 | ||||
| @@ -104,7 +110,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 +147,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 +156,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 +204,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 { | ||||
|   | ||||
							
								
								
									
										225
									
								
								bin/checkver.ps1
									
									
									
									
									
								
							
							
						
						
									
										225
									
								
								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. | ||||
| @@ -60,20 +64,27 @@ 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 | ||||
| $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 = @() | ||||
| @@ -94,7 +105,7 @@ Get-Event | ForEach-Object { | ||||
| $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) { | ||||
| @@ -114,18 +125,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 +169,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 @{ | ||||
| @@ -194,86 +215,97 @@ while ($in_progress -gt 0) { | ||||
|     $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 | ||||
|         $page = $ev.SourceEventArgs.Result | ||||
|         $err = $ev.SourceEventArgs.Error | ||||
|         if ($json.checkver.script) { | ||||
|             $page = $json.checkver.script -join "`r`n" | Invoke-Expression | ||||
|         } | ||||
|  | ||||
|         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) { | ||||
|             # 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 +321,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 +337,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 | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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,56 @@ | ||||
| #requires -v 3 | ||||
| param($cmd) | ||||
| #Requires -Version 5 | ||||
| param($SubCommand) | ||||
|  | ||||
| set-strictmode -off | ||||
| Set-StrictMode -Off | ||||
|  | ||||
| . "$psscriptroot\..\lib\core.ps1" | ||||
| . "$psscriptroot\..\lib\git.ps1" | ||||
| . "$psscriptroot\..\lib\buckets.ps1" | ||||
| . (relpath '..\lib\commands') | ||||
| . "$PSScriptRoot\..\lib\core.ps1" | ||||
| . "$PSScriptRoot\..\lib\buckets.ps1" | ||||
| . "$PSScriptRoot\..\lib\commands.ps1" | ||||
| . "$PSScriptRoot\..\lib\help.ps1" | ||||
|  | ||||
| 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, '--help', '/?') }) { | ||||
|         if (!$SubCommand -and $Args -eq '-v') { | ||||
|             $SubCommand = '--version' | ||||
|         } else { | ||||
|             exec 'help' | ||||
|         } | ||||
|         Pop-Location | ||||
|     } | ||||
|     ({ $SubCommand -eq '--version' }) { | ||||
|         Write-Host 'Current Scoop version:' | ||||
|         if ((Test-CommandAvailable git) -and (Test-Path "$PSScriptRoot\..\.git") -and (get_config SCOOP_BRANCH 'master') -ne 'master') { | ||||
|             Invoke-Expression "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)" | ||||
|         } | ||||
|         Write-Host '' | ||||
|  | ||||
|         Get-LocalBucket | ForEach-Object { | ||||
|             $bucketLoc = Find-BucketDirectory $_ -Root | ||||
|             if ((Test-Path (Join-Path $bucketLoc '.git')) -and (Test-CommandAvailable git)) { | ||||
|                 Write-Host "'$_' bucket:" | ||||
|                 Invoke-Expression "git -C '$bucketLoc' --no-pager log --oneline HEAD -n 1" | ||||
|                 Write-Host '' | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     ({ $SubCommand -in (commands) }) { | ||||
|         if ($Args -in @('-h', '--help', '/?')) { | ||||
|             exec 'help' @($SubCommand) | ||||
|         } else { | ||||
|             exec $SubCommand $Args | ||||
|         } | ||||
|     } | ||||
|     default { | ||||
|         "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/TheRandomLabs/scoop-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,12 +1,4 @@ | ||||
| <# | ||||
| 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 | ||||
|     try { | ||||
| @@ -51,7 +43,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 | ||||
| @@ -152,7 +144,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 +170,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 +276,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 +409,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 | ||||
| } | ||||
|   | ||||
							
								
								
									
										155
									
								
								lib/buckets.ps1
									
									
									
									
									
								
							
							
						
						
									
										155
									
								
								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,57 +72,126 @@ function buckets { | ||||
|     return Get-LocalBucket | ||||
| } | ||||
|  | ||||
| function find_manifest($app, $bucket) { | ||||
|     if ($bucket) { | ||||
|         $manifest = manifest $app $bucket | ||||
|         if ($manifest) { return $manifest, $bucket } | ||||
|         return $null | ||||
| 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 { | ||||
|         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 | ||||
|             $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 | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     foreach($bucket in Get-LocalBucket) { | ||||
|         $manifest = manifest $app $bucket | ||||
|         if($manifest) { return $manifest, $bucket } | ||||
|     return $app, $manifest, $bucket, $url | ||||
| } | ||||
|  | ||||
| function Convert-RepositoryUri { | ||||
|     [CmdletBinding()] | ||||
|     param ( | ||||
|         [Parameter(Mandatory, Position = 0, ValueFromPipeline = $true)] | ||||
|         [String] $Uri | ||||
|     ) | ||||
|  | ||||
|     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) { | ||||
|     if (Test-Path $dir) { | ||||
|         warn "The '$name' bucket already exists. Use 'scoop bucket rm $name' to remove it." | ||||
|         exit 0 | ||||
|         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) { | ||||
|         $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 | ||||
|     } | ||||
|     Write-Host 'OK' | ||||
|  | ||||
|     ensure $bucketsdir | Out-Null | ||||
|     $dir = ensure $dir | ||||
|     git_clone "$repo" "`"$dir`"" -q | ||||
|     git_cmd clone "$repo" "`"$dir`"" -q | ||||
|     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) { | ||||
| @@ -126,18 +199,16 @@ function new_issue_msg($app, $bucket, $title, $body) { | ||||
|     $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 = Invoke-Expression "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 +216,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)) { | ||||
|   | ||||
							
								
								
									
										545
									
								
								lib/core.ps1
									
									
									
									
									
								
							
							
						
						
									
										545
									
								
								lib/core.ps1
									
									
									
									
									
								
							| @@ -41,7 +41,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 +57,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 +117,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 +187,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 +200,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 +217,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, $global) { | ||||
|     if (-not $PSBoundParameters.ContainsKey('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 +249,7 @@ function file_path($app, $file) { | ||||
|  | ||||
| function Get-AppFilePath { | ||||
|     [CmdletBinding()] | ||||
|     [OutputType([String])] | ||||
|     param( | ||||
|         [Parameter(Mandatory = $true, Position = 0)] | ||||
|         [String] | ||||
| @@ -217,14 +260,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 +284,45 @@ 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 | ||||
|     ) | ||||
|  | ||||
|     $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' } | ||||
|     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' } | ||||
|         } | ||||
|  | ||||
|     return $HelperPath | ||||
|         return $HelperPath | ||||
|     } | ||||
| } | ||||
|  | ||||
| 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 +336,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 +407,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 } | ||||
| @@ -429,7 +490,8 @@ function Invoke-ExternalCommand { | ||||
|         return $false | ||||
|     } | ||||
|     if ($LogPath -and ($FilePath -notmatch '(^|\W)msiexec($|\W)')) { | ||||
|         Out-File -FilePath $LogPath -Encoding ASCII -Append -InputObject $Process.StandardOutput.ReadToEnd() | ||||
|         Out-UTF8File -FilePath $LogPath -Append -InputObject $Process.StandardOutput.ReadToEnd() | ||||
|         Out-UTF8File -FilePath $LogPath -Append -InputObject $Process.StandardError.ReadToEnd() | ||||
|     } | ||||
|     $Process.WaitForExit() | ||||
|     if ($Process.ExitCode -ne 0) { | ||||
| @@ -519,95 +581,190 @@ 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 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", | ||||
|             "setlocal enabledelayedexpansion", | ||||
|             "set args=%*", | ||||
|             ":: replace problem characters in arguments", | ||||
|             "set args=%args:`"='%", | ||||
|             "set args=%args:(=``(%", | ||||
|             "set args=%args:)=``)%", | ||||
|             "set invalid=`"='", | ||||
|             "if !args! == !invalid! ( set args= )", | ||||
|             "where /q pwsh.exe", | ||||
|             "if %errorlevel% equ 0 (", | ||||
|             "    pwsh -noprofile -ex unrestricted -file `"$resolved_path`" $arg %args%", | ||||
|             ") else (", | ||||
|             "    powershell -noprofile -ex unrestricted -file `"$resolved_path`" $arg %args%", | ||||
|             ")" | ||||
|         ) -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-Command git -CommandType:Application -ErrorAction:Stop).Source -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 +799,7 @@ function ensure_architecture($architecture_opt) { | ||||
|  | ||||
| function Confirm-InstallationStatus { | ||||
|     [CmdletBinding()] | ||||
|     [OutputType([Object[]])] | ||||
|     param( | ||||
|         [Parameter(Mandatory = $true)] | ||||
|         [String[]] | ||||
| @@ -653,26 +811,29 @@ function Confirm-InstallationStatus { | ||||
|     $Apps | Select-Object -Unique | Where-Object { $_.Name -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 +869,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,55 +896,6 @@ 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 @() } | ||||
| @@ -797,7 +903,7 @@ function applist($apps, $global) { | ||||
| } | ||||
|  | ||||
| function parse_app([string] $app) { | ||||
|     if($app -match '(?:(?<bucket>[a-zA-Z0-9-]+)\/)?(?<app>.*.json$|[a-zA-Z0-9-_.]+)(?:@(?<version>.*))?') { | ||||
|     if($app -match '(?:(?<bucket>[a-zA-Z0-9-]+)\/)?(?<app>.*\.json$|[a-zA-Z0-9-_.]+)(?:@(?<version>.*))?') { | ||||
|         return $matches['app'], $matches['bucket'], $matches['version'] | ||||
|     } | ||||
|     return $app, $null, $null | ||||
| @@ -815,7 +921,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 +937,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 +945,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 +1008,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 +1019,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 +1037,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 +1074,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 # | ||||
| ################## | ||||
|   | ||||
| @@ -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,9 +20,9 @@ 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 | ||||
| @@ -74,9 +37,9 @@ function Expand-7zipArchive { | ||||
|         $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) { | ||||
| @@ -92,7 +55,8 @@ function Expand-7zipArchive { | ||||
|         # Check for tar | ||||
|         $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." | ||||
| @@ -104,6 +68,54 @@ function Expand-7zipArchive { | ||||
|     } | ||||
| } | ||||
|  | ||||
| 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 | ||||
|     } | ||||
| } | ||||
|  | ||||
| function Expand-MsiArchive { | ||||
|     [CmdletBinding()] | ||||
|     param ( | ||||
| @@ -121,7 +133,7 @@ function Expand-MsiArchive { | ||||
|         [Switch] | ||||
|         $Removal | ||||
|     ) | ||||
|     $DestinationPath = $DestinationPath.TrimEnd("\") | ||||
|     $DestinationPath = $DestinationPath.TrimEnd('\') | ||||
|     if ($ExtractDir) { | ||||
|         $OriDestinationPath = $DestinationPath | ||||
|         $DestinationPath = "$DestinationPath\_tmp" | ||||
| @@ -182,9 +194,9 @@ function Expand-InnoArchive { | ||||
|     $LogPath = "$(Split-Path $Path)\innounp.log" | ||||
|     $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 +232,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 | ||||
| @@ -290,23 +275,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 | ||||
| } | ||||
|   | ||||
							
								
								
									
										246
									
								
								lib/depends.ps1
									
									
									
									
									
								
							
							
						
						
									
										246
									
								
								lib/depends.ps1
									
									
									
									
									
								
							| @@ -1,98 +1,170 @@ | ||||
| # 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, $bucket, $null = parse_app $AppName | ||||
|         $Unresolved += $AppName | ||||
|         $null, $manifest, $null, $null = Find-Manifest $AppName $bucket | ||||
|  | ||||
| # 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 { | ||||
|             $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)|(rar)|(iso)|(xz)|(lzh)|(nupkg))(\.[^.]+)?$' | ||||
|         }).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 | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
| } | ||||
							
								
								
									
										431
									
								
								lib/install.ps1
									
									
									
									
									
								
							
							
						
						
									
										431
									
								
								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) { | ||||
| @@ -34,6 +31,19 @@ function install_app($app, $architecture, $global, $suggested, $use_cache = $tru | ||||
|         return | ||||
|     } | ||||
|  | ||||
|     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]" | ||||
|  | ||||
|     $dir = ensure (versiondir $app $version $global) | ||||
| @@ -48,7 +58,6 @@ function install_app($app, $architecture, $global, $suggested, $use_cache = $tru | ||||
|     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 | ||||
|  | ||||
| @@ -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) | ||||
|  | ||||
| @@ -200,12 +177,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 +202,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: " | ||||
|         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-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 | ||||
|             # 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 +352,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 +512,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 +526,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 +596,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 +643,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'." } | ||||
| @@ -819,66 +865,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 +932,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 +981,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 | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -1019,19 +1071,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 +1155,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 +1172,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 +1217,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,6 +1,3 @@ | ||||
| . "$psscriptroot/core.ps1" | ||||
| . "$psscriptroot/autoupdate.ps1" | ||||
|  | ||||
| function manifest_path($app, $bucket) { | ||||
|     fullpath "$(Find-BucketDirectory $bucket)\$(sanitary_path $app).json" | ||||
| } | ||||
| @@ -48,7 +45,7 @@ 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) | ||||
| } | ||||
|  | ||||
| @@ -59,8 +56,20 @@ function install_info($app, $version, $global) { | ||||
| } | ||||
|  | ||||
| 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) { | ||||
| @@ -76,7 +85,7 @@ function supports_architecture($manifest, $architecture) { | ||||
|     return -not [String]::IsNullOrEmpty((arch_specific 'url' $manifest $architecture)) | ||||
| } | ||||
|  | ||||
| function generate_user_manifest($app, $bucket, $version) { | ||||
| function generate_user_manifest($app, $bucket, $version) { # 'autoupdate.ps1' 'buckets.ps1' 'manifest.ps1' | ||||
|     $null, $manifest, $bucket, $null = Find-Manifest $app $bucket | ||||
|     if ("$($manifest.version)" -eq "$version") { | ||||
|         return manifest_path $app $bucket | ||||
| @@ -90,7 +99,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 | ||||
|   | ||||
							
								
								
									
										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 | ||||
|     $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,61 @@ | ||||
| # | ||||
| # To clear everything in your cache, use: | ||||
| #     scoop cache rm * | ||||
| param($cmd, $app) | ||||
|  | ||||
| . "$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 '*') { | ||||
|         $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) | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										25
									
								
								libexec/scoop-cat.ps1
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								libexec/scoop-cat.ps1
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| # 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" # 'Find-Manifest' (indirectly) | ||||
|  | ||||
| if (!$app) { error '<app> missing'; my_usage; exit 1 } | ||||
|  | ||||
| $app, $bucket, $null = parse_app $app | ||||
| $app, $manifest, $bucket, $url = Find-Manifest $app $bucket | ||||
|  | ||||
| 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 | ||||
|  | ||||
| . "$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,7 +17,7 @@ try { | ||||
|     abort "ERROR: $_" | ||||
| } | ||||
|  | ||||
| $deps = @(deps $app $architecture) | ||||
| $deps = @(Get-Dependency $app $architecture) -ne $app | ||||
| if($deps) { | ||||
|     $deps[($deps.length - 1)..0] | ||||
| } | ||||
|   | ||||
							
								
								
									
										134
									
								
								libexec/scoop-download.ps1
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								libexec/scoop-download.ps1
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,134 @@ | ||||
| # 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 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' 'Find-Manifest' (indirectly) | ||||
| . "$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 = Find-Manifest $app $bucket | ||||
|  | ||||
|     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 | ||||
| @@ -2,12 +2,9 @@ | ||||
| # Summary: Exports (an importable) list of installed apps | ||||
| # Help: Lists all installed apps. | ||||
|  | ||||
| . "$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" # 'default_architecture' 'Select-CurrentVersion' (indirectly) | ||||
|  | ||||
| reset_aliases | ||||
| $def_arch = default_architecture | ||||
|  | ||||
| $local = installed_apps $false | ForEach-Object { @{ name = $_; global = $false } } | ||||
| @@ -23,7 +20,7 @@ if($apps) { | ||||
|     $apps | Sort-Object { $_.name } | Where-Object { !$query -or ($_.name -match $query) } | ForEach-Object { | ||||
|         $app = $_.name | ||||
|         $global = $_.global | ||||
|         $ver = current_version $app $global | ||||
|         $ver = Select-CurrentVersion -AppName $app -Global:$global | ||||
|         $global_display = $null; if($global) { $global_display = ' *global*'} | ||||
|  | ||||
|         $install_info = install_info $app $ver $global | ||||
|   | ||||
| @@ -2,12 +2,6 @@ | ||||
| # 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 | ||||
|  | ||||
| @@ -47,4 +41,3 @@ Some useful commands are:" | ||||
| } | ||||
|  | ||||
| exit 0 | ||||
|  | ||||
|   | ||||
| @@ -1,10 +1,10 @@ | ||||
| # Usage: scoop hold <apps> | ||||
| # Summary: Hold an app to disable updates | ||||
|  | ||||
| . "$psscriptroot\..\lib\help.ps1" | ||||
| . "$psscriptroot\..\lib\manifest.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 | ||||
|  | ||||
| if(!$apps) { | ||||
| @@ -21,13 +21,18 @@ $apps | ForEach-Object { | ||||
|         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))} | ||||
|     $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" # 'Find-Manifest' (indirectly) | ||||
|  | ||||
| reset_aliases | ||||
|  | ||||
| if($app) { | ||||
|     $manifest, $bucket = find_manifest $app | ||||
|     if($manifest) { | ||||
|         if([string]::isnullorempty($manifest.homepage)) { | ||||
| if ($app) { | ||||
|     $null, $manifest, $bucket, $null = Find-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 | ||||
|   | ||||
| @@ -1,18 +1,17 @@ | ||||
| # 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" # 'Find-Manifest' (indirectly) | ||||
| . "$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 | ||||
| @@ -27,7 +26,7 @@ if ($app -match '^(ht|f)tps?://|\\\\') { | ||||
|     $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 = Find-Manifest $app $bucket | ||||
| } | ||||
|  | ||||
| if (!$manifest) { | ||||
| @@ -35,105 +34,143 @@ if (!$manifest) { | ||||
| } | ||||
|  | ||||
| $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*" })" } | ||||
|     } | ||||
| } else { | ||||
|     Write-Output "Installed: No" | ||||
|     $item.Installed = $installed_output -join "`n" | ||||
| } | ||||
|  | ||||
| $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 | ||||
|   | ||||
| @@ -13,41 +13,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' '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 +49,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 +86,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 +99,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'." | ||||
|   | ||||
| @@ -4,15 +4,12 @@ | ||||
| # if you've installed 'python' and 'python27', you can use 'scoop reset' to switch between | ||||
| # using one or the other. | ||||
|  | ||||
| . "$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 | ||||
| if($err) { "scoop reset: $err"; exit 1 } | ||||
|  | ||||
| @@ -45,7 +42,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 +64,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,12 +5,9 @@ | ||||
| # 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' | ||||
|  | ||||
| function bin_match($manifest, $query) { | ||||
|     if(!$manifest.bin) { return $false } | ||||
| @@ -44,32 +41,30 @@ function search_bucket($bucket, $query) { | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     $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 | Select-Object -ExpandProperty content | ConvertFrom-Json | ||||
|     $ProgressPreference = 'Continue' | ||||
|     $result | ||||
| } | ||||
|  | ||||
| function github_ratelimit_reached { | ||||
|     $api_link = "https://api.github.com/rate_limit" | ||||
|     $api_link = 'https://api.github.com/rate_limit' | ||||
|     (download_json $api_link).rate.remaining -eq 0 | ||||
| } | ||||
|  | ||||
| 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 | ||||
| @@ -90,7 +85,7 @@ function search_remotes($query) { | ||||
|     } | ||||
|  | ||||
|     $results | ForEach-Object { | ||||
|         "'$($_.bucket)' bucket:" | ||||
|         "'$($_.bucket)' bucket (install using 'scoop install $($_.bucket)/<app>'):" | ||||
|         $_.results | ForEach-Object { "    $_" } | ||||
|         "" | ||||
|     } | ||||
|   | ||||
							
								
								
									
										240
									
								
								libexec/scoop-shim.ps1
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										240
									
								
								libexec/scoop-shim.ps1
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,240 @@ | ||||
| # 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 | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| function Get-ShimTarget($ShimPath) { | ||||
|     if ($ShimPath) { | ||||
|         $shimTarget = if ($ShimPath.EndsWith('.shim')) { | ||||
|             (Get-Content -Path $ShimPath | Select-Object -First 1).Replace('path = ', '').Replace('"', '') | ||||
|         } else { | ||||
|             ((Select-String -Path $ShimPath -Pattern '^(?:@rem|#)\s*(.*)$').Matches.Groups | Select-Object -Index 1).Value | ||||
|         } | ||||
|         if (!$shimTarget) { | ||||
|             $shimTarget = ((Select-String -Path $ShimPath -Pattern '[''"]([^@&]*?)[''"]' -AllMatches).Matches.Groups | Select-Object -Last 1).Value | ||||
|         } | ||||
|         $shimTarget | Convert-Path | ||||
|     } | ||||
| } | ||||
|  | ||||
| switch ($SubCommand) { | ||||
|     'add' { | ||||
|         if ($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,34 +1,24 @@ | ||||
| # Usage: scoop status | ||||
| # Summary: Show status and check for new app versions | ||||
|  | ||||
| . "$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 { | ||||
| if (Test-Path "$currentdir\.git") { | ||||
|     git_cmd -C "`"$currentdir`"" fetch -q origin | ||||
|     $commits = $(git -C $currentdir log "HEAD..origin/$(get_config SCOOP_BRANCH)" --oneline) | ||||
|     if ($commits) { $needs_update = $true } | ||||
| } else { | ||||
|     $needs_update = $true | ||||
| } | ||||
|  | ||||
| if($needs_update) { | ||||
| if ($needs_update) { | ||||
|     warn "Scoop is out of date. Run 'scoop update' to get the latest changes." | ||||
| } | ||||
| else { success "Scoop is up to date."} | ||||
| } else { success 'Scoop is up to date.' } | ||||
|  | ||||
| $failed = @() | ||||
| $outdated = @() | ||||
| @@ -39,69 +29,69 @@ $onhold = @() | ||||
| $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) { | ||||
|         if ($status.failed) { | ||||
|             $failed += @{ $app = $status.version } | ||||
|         } | ||||
|         if($status.removed) { | ||||
|         if ($status.removed) { | ||||
|             $removed += @{ $app = $status.version } | ||||
|         } | ||||
|         if($status.outdated) { | ||||
|         if ($status.outdated) { | ||||
|             $outdated += @{ $app = @($status.version, $status.latest_version) } | ||||
|             if($status.hold) { | ||||
|             if ($status.hold) { | ||||
|                 $onhold += @{ $app = @($status.version, $status.latest_version) } | ||||
|             } | ||||
|         } | ||||
|         if($status.missing_deps) { | ||||
|             $missing_deps += ,(@($app) + @($status.missing_deps)) | ||||
|         if ($status.missing_deps) { | ||||
|             $missing_deps += , (@($app) + @($status.missing_deps)) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| if($outdated) { | ||||
|     write-host -f DarkCyan 'Updates are available for:' | ||||
| if ($outdated) { | ||||
|     Write-Host -f DarkCyan 'Updates are available for:' | ||||
|     $outdated.keys | ForEach-Object { | ||||
|         $versions = $outdated.$_ | ||||
|         "    $_`: $($versions[0]) -> $($versions[1])" | ||||
|     } | ||||
| } | ||||
|  | ||||
| if($onhold) { | ||||
|     write-host -f DarkCyan 'These apps are outdated and on hold:' | ||||
| 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:' | ||||
| 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:' | ||||
| 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:' | ||||
| 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!" | ||||
| if (!$old -and !$removed -and !$failed -and !$missing_deps -and !$needs_update) { | ||||
|     success 'Everything is ok!' | ||||
| } | ||||
|  | ||||
| exit 0 | ||||
|   | ||||
| @@ -1,10 +1,10 @@ | ||||
| # Usage: scoop unhold <app> | ||||
| # Summary: Unhold an app to enable updates | ||||
|  | ||||
| . "$psscriptroot\..\lib\help.ps1" | ||||
| . "$psscriptroot\..\lib\manifest.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 | ||||
|  | ||||
| if(!$apps) { | ||||
| @@ -21,13 +21,18 @@ $apps | ForEach-Object { | ||||
|         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))} | ||||
|     $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" # '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,75 @@ 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 | ||||
|  | ||||
|     try { | ||||
|         Test-Path $dir -ErrorAction Stop | Out-Null | ||||
|     } catch [UnauthorizedAccessException] { | ||||
|         error "Access denied: $dir. You might need to restart." | ||||
|         continue | ||||
|     } | ||||
|  | ||||
|     $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 | ||||
|         } | ||||
|  | ||||
|         $manifest = installed_manifest $app $version $global | ||||
|         $install = install_info $app $version $global | ||||
|         $architecture = $install.architecture | ||||
|  | ||||
|         run_uninstaller $manifest $architecture $dir | ||||
|         rm_shims $app $manifest $global $architecture | ||||
|         rm_startmenu_shortcuts $manifest $global $architecture | ||||
|  | ||||
|         # 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 | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     # 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,36 +50,29 @@ 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")) { | ||||
|     if (!(Test-Path "$currentdir\.git")) { | ||||
|         $newdir = fullpath $(versiondir 'scoop' 'new') | ||||
|  | ||||
|         # 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")) { | ||||
|         if (!(Test-Path "$newdir")) { | ||||
|             abort 'Scoop update failed.' | ||||
|         } | ||||
|  | ||||
| @@ -89,69 +80,73 @@ function update_scoop() { | ||||
|         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 = Invoke-Expression "git -C '$currentdir' rev-parse HEAD" | ||||
|         $currentRepo = Invoke-Expression "git -C '$currentdir' config remote.origin.url" | ||||
|         $currentBranch = Invoke-Expression "git -C '$currentdir' branch" | ||||
|  | ||||
|         $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" | ||||
|             Invoke-Expression "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/*" | ||||
|             Invoke-Expression "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 | ||||
|             Invoke-Expression "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 | ||||
|             Invoke-Expression "git -C '$currentdir' --no-pager log --no-decorate --grep='^(chore)' --invert-grep --format='tformat: * %C(yellow)%h%Creset %<|(72,trunc)%s %C(cyan)%cr%Creset' '$previousCommit..HEAD'" | ||||
|         } | ||||
|  | ||||
|         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..." | ||||
|                 rm_bucket 'main' | ||||
|                 add_bucket 'main' | ||||
|             } else { | ||||
|                 Write-Host "'$bucket' is not a git repository. Skipped." | ||||
|             } | ||||
|             continue | ||||
|         } | ||||
|  | ||||
|         Push-Location $loc | ||||
|         git_pull -q | ||||
|         $previousCommit = (Invoke-Expression "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 | ||||
|             Invoke-Expression "git -C '$bucketLoc' --no-pager log --no-decorate --grep='^(chore)' --invert-grep --format='tformat: * %C(yellow)%h%Creset %<|(72,trunc)%s %C(cyan)%cr%Creset' '$previousCommit..HEAD'" | ||||
|         } | ||||
|         Pop-Location | ||||
|     } | ||||
|  | ||||
|     set_config lastupdate ([System.DateTime]::Now.ToString('o')) | Out-Null | ||||
| @@ -159,7 +154,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 +166,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 +186,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 +207,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 +224,19 @@ function update($app, $global, $quiet = $false, $independent, $suggested, $use_c | ||||
|     # endregion Workaround | ||||
|  | ||||
|     $dir = versiondir $app $old_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." | ||||
|     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 | ||||
| @@ -277,15 +263,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 { | ||||
| @@ -299,7 +295,7 @@ if (!$apps) { | ||||
|     $outdated = @() | ||||
|     $apps_param = $apps | ||||
|  | ||||
|     if ($apps_param -eq '*') { | ||||
|     if ($apps_param -eq '*' -or $all) { | ||||
|         $apps = applist (installed_apps $false) $false | ||||
|         if ($global) { | ||||
|             $apps += applist (installed_apps $true) $true | ||||
| @@ -311,28 +307,34 @@ 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 '*') { | ||||
|                 write-host -f green "$app`: $($status.version) (latest version)" | ||||
|                 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.' | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         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:" | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -28,40 +28,40 @@ | ||||
| # | ||||
| # 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. | ||||
| #   -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 | ||||
|  | ||||
| . "$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" # 'Find-Manifest' (indirectly) | ||||
| . "$PSScriptRoot\..\lib\json.ps1" # 'json_path' | ||||
| . "$PSScriptRoot\..\lib\install.ps1" # 'hash_for_url' | ||||
| . "$PSScriptRoot\..\lib\depends.ps1" # 'Get-Dependency' | ||||
|  | ||||
| reset_aliases | ||||
|  | ||||
| $opt, $apps, $err = getopt $args 'a:sn' @('arch=', 'scan', 'no-depends') | ||||
| if($err) { "scoop virustotal: $err"; exit 1 } | ||||
| if(!$apps) { my_usage; exit 1 } | ||||
| $opt, $apps, $err = getopt $args 'a:snu' @('arch=', 'scan', 'no-depends', 'no-update-scoop') | ||||
| if ($err) { "scoop virustotal: $err"; exit 1 } | ||||
| if (!$apps) { my_usage; exit 1 } | ||||
| $architecture = ensure_architecture ($opt.a + $opt.arch) | ||||
|  | ||||
| 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 '*') { | ||||
|     $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 | ||||
| @@ -93,13 +93,13 @@ Function Get-VirusTotalResult($hash, $app) { | ||||
|     $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" } | ||||
|         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) { | ||||
|     Write-Host -f $fg "$app`: $unsafe/$undetected, $see_url" | ||||
|     if ($unsafe -gt 0) { | ||||
|         return $_ERR_UNSAFE | ||||
|     } | ||||
|     return 0 | ||||
| @@ -126,16 +126,15 @@ Function Submit-RedirectedUrl { | ||||
|     # Adapted according to Roy's response (January 23, 2014 at 11:59 am) | ||||
|     # Adapted to always return an URL | ||||
|     Param ( | ||||
|         [Parameter(Mandatory=$true)] | ||||
|         [Parameter(Mandatory = $true)] | ||||
|         [String]$URL | ||||
|     ) | ||||
|     $request = [System.Net.WebRequest]::Create($url) | ||||
|     $request.AllowAutoRedirect=$false | ||||
|     $response=$request.GetResponse() | ||||
|     $request.AllowAutoRedirect = $false | ||||
|     $response = $request.GetResponse() | ||||
|     if (([int]$response.StatusCode -ge 300) -and ([int]$response.StatusCode -lt 400)) { | ||||
|         $redir = $response.GetResponseHeader("Location") | ||||
|     } | ||||
|     else { | ||||
|         $redir = $response.GetResponseHeader('Location') | ||||
|     } else { | ||||
|         $redir = $URL | ||||
|     } | ||||
|     $response.Close() | ||||
| @@ -152,8 +151,8 @@ 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") | ||||
| 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.  " + | ||||
| @@ -203,14 +202,14 @@ Function Submit-ToVirusTotal ($url, $app, $do_scan, $retrying=$False) { | ||||
| $apps | ForEach-Object { | ||||
|     $app = $_ | ||||
|     # write-host $app | ||||
|     $manifest, $bucket = find_manifest $app | ||||
|     $null, $manifest, $bucket, $null = Find-Manifest $app | ||||
|     if(!$manifest) { | ||||
|         $exit_code = $exit_code -bor $_ERR_NO_INFO | ||||
|         warn "$app`: manifest not found" | ||||
|         return | ||||
|     } | ||||
|  | ||||
|     $urls = url $manifest $architecture | ||||
|     $urls = script:url $manifest $architecture | ||||
|     $urls | ForEach-Object { | ||||
|         $url = $_ | ||||
|         $hash = hash_for_url $manifest $url $architecture | ||||
|   | ||||
| @@ -2,40 +2,45 @@ | ||||
| # 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 } | ||||
| if (!$command) { | ||||
|     'ERROR: <command> missing' | ||||
|     my_usage | ||||
|     exit 1 | ||||
| } | ||||
|  | ||||
| try { | ||||
|     $gcm = Get-Command "$command" -ea stop | ||||
|     $gcm = Get-Command "$command" -ErrorAction Stop | ||||
| } catch { | ||||
|     abort "'$command' not found" 3 | ||||
| } | ||||
|  | ||||
| $path = "$($gcm.path)" | ||||
| $usershims = "$(resolve-path $(shimdir $false))" | ||||
| $path = $gcm.Path | ||||
| $usershims = Convert-Path (shimdir $false) | ||||
| $globalshims = fullpath (shimdir $true) # don't resolve: may not exist | ||||
|  | ||||
| if($path.endswith(".ps1") -and ($path -like "$usershims*" -or $path -like "$globalshims*")) { | ||||
|     $shimtext = Get-Content $path | ||||
| if ($path -like "$usershims*" -or $path -like "$globalshims*") { | ||||
|     $exepath = if ($path.EndsWith('.exe') -or $path.EndsWith('.shim')) { | ||||
|         (Get-Content ($path -replace '\.exe$', '.shim') | Select-Object -First 1).Replace('path = ', '').Replace('"', '') | ||||
|     } else { | ||||
|         ((Select-String -Path $path -Pattern '^(?:@rem|#)\s*(.*)$').Matches.Groups | Select-Object -Index 1).Value | ||||
|     } | ||||
|     if (!$exepath) { | ||||
|         $exepath = ((Select-String -Path $path -Pattern '[''"]([^@&]*?)[''"]' -AllMatches).Matches.Groups | Select-Object -Last 1).Value | ||||
|     } | ||||
|  | ||||
|     $exepath = ($shimtext | Where-Object { $_.startswith('$path') }).split(' ') | Select-Object -Last 1 | Invoke-Expression | ||||
|  | ||||
|     if(![system.io.path]::ispathrooted($exepath)) { | ||||
|     if (![System.IO.Path]::IsPathRooted($exepath)) { | ||||
|         # Expand relative path | ||||
|         $exepath = resolve-path (join-path (split-path $path) $exepath) | ||||
|         $exepath = Convert-Path $exepath | ||||
|     } | ||||
|  | ||||
|     friendly_path $exepath | ||||
| } elseif($gcm.commandtype -eq 'Application') { | ||||
| } elseif ($gcm.CommandType -eq 'Application') { | ||||
|     $gcm.Source | ||||
| } elseif($gcm.commandtype -eq 'Alias') { | ||||
|     scoop which $gcm.resolvedcommandname | ||||
| } elseif ($gcm.CommandType -eq 'Alias') { | ||||
|     scoop which $gcm.ResolvedCommandName | ||||
| } else { | ||||
|     [console]::error.writeline("Not a scoop shim.") | ||||
|     Write-Host 'Not a scoop shim.' | ||||
|     $path | ||||
|     exit 2 | ||||
| } | ||||
|   | ||||
							
								
								
									
										225
									
								
								schema.json
									
									
									
									
									
								
							
							
						
						
									
										225
									
								
								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": { | ||||
| @@ -155,63 +172,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 +286,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 +305,10 @@ | ||||
|                         }, | ||||
|                         "useragent": { | ||||
|                             "type": "string" | ||||
|                         }, | ||||
|                         "script": { | ||||
|                             "$ref": "#/definitions/stringOrArrayOfStrings", | ||||
|                             "description": "Custom PowerShell script to retrieve application version using more complex approach." | ||||
|                         } | ||||
|                     }, | ||||
|                     "type": "object" | ||||
| @@ -365,6 +414,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 +456,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 +548,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", | ||||
| @@ -525,12 +598,14 @@ | ||||
|             "$ref": "#/definitions/uriOrArrayOfUris" | ||||
|         }, | ||||
|         "version": { | ||||
|             "pattern": "^[\\w\\.\\-_]+$", | ||||
|             "pattern": "^[\\w\\.\\-+_]+$", | ||||
|             "type": "string" | ||||
|         } | ||||
|     }, | ||||
|     "required": [ | ||||
|         "version" | ||||
|         "version", | ||||
|         "homepage", | ||||
|         "license" | ||||
|     ], | ||||
|     "title": "scoop app manifest schema", | ||||
|     "type": "object" | ||||
|   | ||||
							
								
								
									
										64
									
								
								supporting/formats/ScoopTypes.Format.ps1xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								supporting/formats/ScoopTypes.Format.ps1xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | ||||
| <?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> | ||||
|     </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,21 @@ | ||||
| Param([Switch]$Fast) | ||||
| Push-Location $psscriptroot | ||||
| . "$psscriptroot\..\..\lib\install.ps1" | ||||
| Push-Location $PSScriptRoot | ||||
| . "$PSScriptRoot\..\..\lib\install.ps1" | ||||
|  | ||||
| if(!$Fast) { | ||||
| if (!$Fast) { | ||||
|     Write-Host "Install dependencies ..." | ||||
|     Invoke-Expression "$psscriptroot\install.ps1" | ||||
|     Invoke-Expression "$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.0.1" targetFramework="net45" developmentDependency="true" /> | ||||
| </packages> | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| <?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="packages\Microsoft.Net.Compilers.Toolset.4.0.1\build\Microsoft.Net.Compilers.Toolset.props" Condition="Exists('packages\Microsoft.Net.Compilers.Toolset.4.0.1\build\Microsoft.Net.Compilers.Toolset.props')" /> | ||||
|   <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> | ||||
|   <PropertyGroup> | ||||
|     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | ||||
| @@ -31,6 +31,6 @@ | ||||
|     <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.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'))" /> | ||||
|     <Error Condition="!Exists('packages\Microsoft.Net.Compilers.Toolset.4.0.1\build\Microsoft.Net.Compilers.Toolset.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.Net.Compilers.Toolset.4.0.1\build\Microsoft.Net.Compilers.Toolset.props'))" /> | ||||
|   </Target> | ||||
| </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 | ||||
| 7f912b28a07c226e0be3acfb2f57f050538aba0100fa1f0bf2c39f1a1f1da814 *Newtonsoft.Json.dll | ||||
| cff8fc4ce358d7daff84ab47129a776797a4ec819c1586a15bd5e63144f5b73f *Newtonsoft.Json.Schema.dll | ||||
| 0d6b228378cbabff23a30456d22f1a31337c466f90cf8b7997cc48bd171155f3 *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 | ||||
| 3398094ce429ab5dcdecf2ad04803230669bb4accaef7083992e9b87afac55841ba8def2a5168358bd17e60799e55d076b0e5ca44c86b9e6c91150d3dc37c721 *Newtonsoft.Json.dll | ||||
| 298d3d0b656acbb1fe5ed0c3abb49a640c47889184ab7bd4b594e51a7d7f829d5c8685edbd10a286fd56bfd8d601b9f187da463a5a9c8509365eddaea280642f *Newtonsoft.Json.Schema.dll | ||||
| afabe1df6ab837395a5da5ec8dd12bf3f36a8512b76e6f751c14045544246980e9d4061d437792836db792864b7db2761e84f1bf65bac688657a862b68fc7b45 *Scoop.Validator.dll | ||||
| d497c27b48f44f4cff270d3c8801b0cecc74108f8786a4a7c40e57541308ae33a69f5456cfc43ae1ce4214038d20da9fbeac1bcf76cc58d972863b58dab18401 *validator.exe | ||||
|   | ||||
										
											Binary file not shown.
										
									
								
							| @@ -1,26 +1,26 @@ | ||||
| Param([Switch]$Fast) | ||||
| Push-Location $psscriptroot | ||||
| . "$psscriptroot\..\..\lib\install.ps1" | ||||
| Push-Location $PSScriptRoot | ||||
| . "$PSScriptRoot\..\..\lib\install.ps1" | ||||
|  | ||||
| if(!$Fast) { | ||||
| if (!$Fast) { | ||||
|     Write-Host "Install dependencies ..." | ||||
|     Invoke-Expression "$psscriptroot\install.ps1" | ||||
|     Invoke-Expression "$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="12.0.3" targetFramework="net45" /> | ||||
|   <package id="Newtonsoft.Json.Schema" version="3.0.14" targetFramework="net45" /> | ||||
|   <package id="Microsoft.Net.Compilers.Toolset" version="4.0.1" targetFramework="net45" developmentDependency="true" /> | ||||
| </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,6 +1,6 @@ | ||||
| <?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="packages\Microsoft.Net.Compilers.Toolset.4.0.1\build\Microsoft.Net.Compilers.Toolset.props" Condition="Exists('packages\Microsoft.Net.Compilers.Toolset.4.0.1\build\Microsoft.Net.Compilers.Toolset.props')" /> | ||||
|   <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> | ||||
|   <PropertyGroup> | ||||
|     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | ||||
| @@ -14,12 +14,12 @@ | ||||
|     <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> | ||||
|     <Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed"> | ||||
|       <HintPath>packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll</HintPath> | ||||
|       <Private>True</Private> | ||||
|     </Reference> | ||||
|     <Reference Include="Newtonsoft.Json.Schema, Version=3.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed"> | ||||
|       <HintPath>packages\Newtonsoft.Json.Schema.3.0.10\lib\net45\Newtonsoft.Json.Schema.dll</HintPath> | ||||
|       <HintPath>packages\Newtonsoft.Json.Schema.3.0.14\lib\net45\Newtonsoft.Json.Schema.dll</HintPath> | ||||
|       <Private>True</Private> | ||||
|     </Reference> | ||||
|     <Reference Include="System" /> | ||||
| @@ -43,6 +43,6 @@ | ||||
|     <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.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'))" /> | ||||
|     <Error Condition="!Exists('packages\Microsoft.Net.Compilers.Toolset.4.0.1\build\Microsoft.Net.Compilers.Toolset.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.Net.Compilers.Toolset.4.0.1\build\Microsoft.Net.Compilers.Toolset.props'))" /> | ||||
|   </Target> | ||||
| </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 '(unformated)' } | ||||
|     ) | ||||
|  | ||||
|     $files_exist = ($files.Count -gt 0) | ||||
|  | ||||
|     it $('non-binary project files exist ({0} found)' -f $files.Count) -skip:$(-not $files_exist) { | ||||
|         if (-not ($files.Count -gt 0)) | ||||
|         { | ||||
|             throw "No non-binary project were found" | ||||
|     It $('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!"' | ||||
|             Invoke-Expression $alias_file | Should -Be 'hello, world!' | ||||
|         } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   context "alias exists" { | ||||
|     it "does not change existing alias" { | ||||
|       $alias_file = "$shimdir\scoop-rm.ps1" | ||||
|       new-item $alias_file -type file | ||||
|       $alias_file | should -exist | ||||
|     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 | ||||
|         } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,81 +1,79 @@ | ||||
| . "$PSScriptRoot\Scoop-TestLib.ps1" | ||||
| . "$PSScriptRoot\..\lib\core.ps1" | ||||
|  | ||||
| Describe "config" -Tag 'Scoop' { | ||||
| Describe 'config' -Tag 'Scoop' { | ||||
|     BeforeAll { | ||||
|         $json = '{ "one": 1, "two": [ { "a": "a" }, "b", 2 ], "three": { "four": 4 }, "five": true, "six": false, "seven": "\/Date(1529917395805)\/", "eight": "2019-03-18T15:22:09.3930000+00:00" }' | ||||
|     } | ||||
|  | ||||
|     It "converts JSON to PSObject" { | ||||
|         $obj = ConvertFrom-Json $json | ||||
|  | ||||
|         $obj.one | Should -BeExactly 1 | ||||
|         $obj.two[0].a | Should -Be "a" | ||||
|         $obj.two[1] | Should -Be "b" | ||||
|         $obj.two[2] | Should -BeExactly 2 | ||||
|         $obj.three.four | Should -BeExactly 4 | ||||
|         $obj.five | Should -BeTrue | ||||
|         $obj.six | Should -BeFalse | ||||
|         $obj.seven | Should -BeOfType [System.DateTime] | ||||
|         if($PSVersionTable.PSVersion.Major -lt 6) { | ||||
|             $obj.eight | Should -BeOfType [System.String] | ||||
|         } else { | ||||
|             $obj.eight | Should -BeOfType [System.DateTime] | ||||
|         $configFile = "$env:TEMP\ScoopTestFixtures\config.json" | ||||
|         if (Test-Path $configFile) { | ||||
|             Remove-Item -Path $configFile -Force | ||||
|         } | ||||
|         $unicode = [Regex]::Unescape('\u4f60\u597d\u3053\u3093\u306b\u3061\u306f') # 你好こんにちは | ||||
|     } | ||||
|  | ||||
|     It "load_config should return PSObject" { | ||||
|         Mock Get-Content { $json } | ||||
|         Mock Test-Path { $true } | ||||
|         (load_cfg 'file') | Should -Not -BeNullOrEmpty | ||||
|         (load_cfg 'file') | Should -BeOfType [System.Management.Automation.PSObject] | ||||
|         (load_cfg 'file').one | Should -BeExactly 1 | ||||
|     } | ||||
|  | ||||
|     It "get_config should return exactly the same values" { | ||||
|         $scoopConfig = ConvertFrom-Json $json | ||||
|         get_config 'does_not_exist' 'default' | Should -Be 'default' | ||||
|  | ||||
|         get_config 'one' | Should -BeExactly 1 | ||||
|         (get_config 'two')[0].a | Should -Be "a" | ||||
|         (get_config 'two')[1] | Should -Be "b" | ||||
|         (get_config 'two')[2] | Should -BeExactly 2 | ||||
|         (get_config 'three').four | Should -BeExactly 4 | ||||
|         get_config 'five' | Should -BeTrue | ||||
|         get_config 'six' | Should -BeFalse | ||||
|         get_config 'seven' | Should -BeOfType [System.DateTime] | ||||
|         if($PSVersionTable.PSVersion.Major -lt 6) { | ||||
|             get_config 'eight' | Should -BeOfType [System.String] | ||||
|         } else { | ||||
|             get_config 'eight' | Should -BeOfType [System.DateTime] | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     It "set_config should create a new PSObject and ensure existing directory" { | ||||
|     BeforeEach { | ||||
|         $scoopConfig = $null | ||||
|         $configFile = "$PSScriptRoot\.scoop" | ||||
|  | ||||
|         Mock ensure { $PSScriptRoot } -Verifiable -ParameterFilter { $dir -eq (Split-Path -Path $configFile) } | ||||
|         Mock Set-Content {} -Verifiable -ParameterFilter { $Path -eq $configFile } | ||||
|         Mock ConvertTo-Json { '' } -Verifiable -ParameterFilter { $InputObject -is [System.Management.Automation.PSObject] } | ||||
|  | ||||
|         set_config 'does_not_exist' 'default' | ||||
|  | ||||
|         Assert-VerifiableMock | ||||
|     } | ||||
|  | ||||
|     It "set_config should remove a value if set to `$null" { | ||||
|         $scoopConfig = New-Object PSObject | ||||
|         $scoopConfig | Add-Member -MemberType NoteProperty -Name 'should_be_removed' -Value 'a_value' | ||||
|         $scoopConfig | Add-Member -MemberType NoteProperty -Name 'should_stay' -Value 'another_value' | ||||
|         $configFile = "$PSScriptRoot\.scoop" | ||||
|     It 'load_cfg should return null if config file does not exist' { | ||||
|         load_cfg $configFile | Should -Be $null | ||||
|     } | ||||
|  | ||||
|         Mock Set-Content {} -Verifiable -ParameterFilter { $Path -eq $configFile } | ||||
|         Mock ConvertTo-Json { '' } -Verifiable -ParameterFilter { $InputObject -is [System.Management.Automation.PSObject] } | ||||
|     It 'set_config should be able to save typed values correctly' { | ||||
|         # number | ||||
|         $scoopConfig = set_config 'one' 1 | ||||
|         $scoopConfig.one | Should -BeExactly 1 | ||||
|  | ||||
|         $scoopConfig = set_config 'should_be_removed' $null | ||||
|         $scoopConfig.should_be_removed | Should -BeNullOrEmpty | ||||
|         $scoopConfig.should_stay | Should -Be 'another_value' | ||||
|         # boolean | ||||
|         $scoopConfig = set_config 'two' $true | ||||
|         $scoopConfig.two | Should -BeTrue | ||||
|         $scoopConfig = set_config 'three' $false | ||||
|         $scoopConfig.three | Should -BeFalse | ||||
|  | ||||
|         Assert-VerifiableMock | ||||
|         # underline key | ||||
|         $scoopConfig = set_config 'under_line' 'four' | ||||
|         $scoopConfig.under_line | Should -BeExactly 'four' | ||||
|  | ||||
|         # string | ||||
|         $scoopConfig = set_config 'five' 'not null' | ||||
|  | ||||
|         # datetime | ||||
|         $scoopConfig = set_config 'time' ([System.DateTime]::Parse('2019-03-18T15:22:09.3930000+00:00', $null, [System.Globalization.DateTimeStyles]::AdjustToUniversal)) | ||||
|         $scoopConfig.time | Should -BeOfType [System.DateTime] | ||||
|  | ||||
|         # non-ASCII | ||||
|         $scoopConfig = set_config 'unicode' $unicode | ||||
|         $scoopConfig.unicode | Should -Be $unicode | ||||
|     } | ||||
|  | ||||
|     It 'load_cfg should return PSObject if config file exist' { | ||||
|         $scoopConfig = load_cfg $configFile | ||||
|         $scoopConfig | Should -Not -BeNullOrEmpty | ||||
|         $scoopConfig | Should -BeOfType [System.Management.Automation.PSObject] | ||||
|         $scoopConfig.one | Should -BeExactly 1 | ||||
|         $scoopConfig.two | Should -BeTrue | ||||
|         $scoopConfig.three | Should -BeFalse | ||||
|         $scoopConfig.under_line | Should -BeExactly 'four' | ||||
|         $scoopConfig.five | Should -Be 'not null' | ||||
|         $scoopConfig.time | Should -BeOfType [System.DateTime] | ||||
|         $scoopConfig.time | Should -Be ([System.DateTime]::Parse('2019-03-18T15:22:09.3930000+00:00', $null, [System.Globalization.DateTimeStyles]::AdjustToUniversal)) | ||||
|         $scoopConfig.unicode | Should -Be $unicode | ||||
|     } | ||||
|  | ||||
|     It 'get_config should return exactly the same values' { | ||||
|         $scoopConfig = load_cfg $configFile | ||||
|         (get_config 'one') | Should -BeExactly 1 | ||||
|         (get_config 'two') | Should -BeTrue | ||||
|         (get_config 'three') | Should -BeFalse | ||||
|         (get_config 'under_line') | Should -BeExactly 'four' | ||||
|         (get_config 'five') | Should -Be 'not null' | ||||
|         (get_config 'time') | Should -BeOfType [System.DateTime] | ||||
|         (get_config 'time') | Should -Be ([System.DateTime]::Parse('2019-03-18T15:22:09.3930000+00:00', $null, [System.Globalization.DateTimeStyles]::AdjustToUniversal)) | ||||
|         (get_config 'unicode') | Should -Be $unicode | ||||
|     } | ||||
|  | ||||
|     It 'set_config should remove a value if being set to $null' { | ||||
|         $scoopConfig = load_cfg $configFile | ||||
|         $scoopConfig = set_config 'five' $null | ||||
|         $scoopConfig.five | Should -BeNullOrEmpty | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,87 +1,87 @@ | ||||
| . "$psscriptroot\..\lib\core.ps1" | ||||
| . "$psscriptroot\..\lib\install.ps1" | ||||
| . "$psscriptroot\..\lib\unix.ps1" | ||||
| . "$psscriptroot\Scoop-TestLib.ps1" | ||||
| . "$PSScriptRoot\Scoop-TestLib.ps1" | ||||
| . "$PSScriptRoot\..\lib\core.ps1" | ||||
| . "$PSScriptRoot\..\lib\install.ps1" | ||||
| . "$PSScriptRoot\..\lib\unix.ps1" | ||||
|  | ||||
| $repo_dir = (Get-Item $MyInvocation.MyCommand.Path).directory.parent.FullName | ||||
| $isUnix = is_unix | ||||
|  | ||||
| describe "Get-AppFilePath" -Tag 'Scoop' { | ||||
|     beforeall { | ||||
|         $working_dir = setup_working "is_directory" | ||||
|         Mock versiondir { 'local' } -Verifiable -ParameterFilter { $global -eq $false } | ||||
|         Mock versiondir { 'global' } -Verifiable -ParameterFilter { $global -eq $true } | ||||
| Describe 'Get-AppFilePath' -Tag 'Scoop' { | ||||
|     BeforeAll { | ||||
|         $working_dir = setup_working 'is_directory' | ||||
|         Mock currentdir { 'local' } -Verifiable -ParameterFilter { $global -eq $false } | ||||
|         Mock currentdir { 'global' } -Verifiable -ParameterFilter { $global -eq $true } | ||||
|     } | ||||
|  | ||||
|     it "should return locally installed program" { | ||||
|     It 'should return locally installed program' { | ||||
|         Mock Test-Path { $true } -Verifiable -ParameterFilter { $Path -eq 'local\i_am_a_file.txt' } | ||||
|         Mock Test-Path { $false } -Verifiable -ParameterFilter { $Path -eq 'global\i_am_a_file.txt' } | ||||
|         Get-AppFilePath -App 'is_directory' -File 'i_am_a_file.txt' | Should -Be 'local\i_am_a_file.txt' | ||||
|     } | ||||
|  | ||||
|     it "should return globally installed program" { | ||||
|     It 'should return globally installed program' { | ||||
|         Mock Test-Path { $false } -Verifiable -ParameterFilter { $Path -eq 'local\i_am_a_file.txt' } | ||||
|         Mock Test-Path { $true } -Verifiable -ParameterFilter { $Path -eq 'global\i_am_a_file.txt' } | ||||
|         Get-AppFilePath -App 'is_directory' -File 'i_am_a_file.txt' | Should -Be 'global\i_am_a_file.txt' | ||||
|     } | ||||
|  | ||||
|     it "should return null if program is not installed" { | ||||
|     It 'should return null if program is not installed' { | ||||
|         Get-AppFilePath -App 'is_directory' -File 'i_do_not_exist' | Should -BeNullOrEmpty | ||||
|     } | ||||
|  | ||||
|     it "should throw if parameter is wrong or missing" { | ||||
|     It 'should throw if parameter is wrong or missing' { | ||||
|         { Get-AppFilePath -App 'is_directory' -File } | Should -Throw | ||||
|         { Get-AppFilePath -App -File 'i_am_a_file.txt' } | Should -Throw | ||||
|         { Get-AppFilePath -App -File } | Should -Throw | ||||
|     } | ||||
| } | ||||
|  | ||||
| describe "Get-HelperPath" -Tag 'Scoop' { | ||||
|     beforeall { | ||||
|         $working_dir = setup_working "is_directory" | ||||
| Describe 'Get-HelperPath' -Tag 'Scoop' { | ||||
|     BeforeAll { | ||||
|         $working_dir = setup_working 'is_directory' | ||||
|     } | ||||
|     it "should return path if program is installed" { | ||||
|     It 'should return path if program is installed' { | ||||
|         Mock Get-AppFilePath { '7zip\current\7z.exe' } | ||||
|         Get-HelperPath -Helper 7zip | Should -Be '7zip\current\7z.exe' | ||||
|     } | ||||
|  | ||||
|     it "should return null if program is not installed" { | ||||
|     It 'should return null if program is not installed' { | ||||
|         Mock Get-AppFilePath { $null } | ||||
|         Get-HelperPath -Helper 7zip | Should -BeNullOrEmpty | ||||
|     } | ||||
|  | ||||
|     it "should throw if parameter is wrong or missing" { | ||||
|     It 'should throw if parameter is wrong or missing' { | ||||
|         { Get-HelperPath -Helper } | Should -Throw | ||||
|         { Get-HelperPath -Helper Wrong } | Should -Throw | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| describe "Test-HelperInstalled" -Tag 'Scoop' { | ||||
|     it "should return true if program is installed" { | ||||
| Describe 'Test-HelperInstalled' -Tag 'Scoop' { | ||||
|     It 'should return true if program is installed' { | ||||
|         Mock Get-HelperPath { '7z.exe' } | ||||
|         Test-HelperInstalled -Helper 7zip | Should -BeTrue | ||||
|     } | ||||
|  | ||||
|     it "should return false if program is not installed" { | ||||
|     It 'should return false if program is not installed' { | ||||
|         Mock Get-HelperPath { $null } | ||||
|         Test-HelperInstalled -Helper 7zip | Should -BeFalse | ||||
|     } | ||||
|  | ||||
|     it "should throw if parameter is wrong or missing" { | ||||
|     It 'should throw if parameter is wrong or missing' { | ||||
|         { Test-HelperInstalled -Helper } | Should -Throw | ||||
|         { Test-HelperInstalled -Helper Wrong } | Should -Throw | ||||
|     } | ||||
| } | ||||
|  | ||||
| describe "Test-Aria2Enabled" -Tag 'Scoop' { | ||||
|     it "should return true if aria2 is installed" { | ||||
| Describe 'Test-Aria2Enabled' -Tag 'Scoop' { | ||||
|     It 'should return true if aria2 is installed' { | ||||
|         Mock Test-HelperInstalled { $true } | ||||
|         Mock get_config { $true } | ||||
|         Test-Aria2Enabled | Should -BeTrue | ||||
|     } | ||||
|  | ||||
|     it "should return false if aria2 is not installed" { | ||||
|     It 'should return false if aria2 is not installed' { | ||||
|         Mock Test-HelperInstalled { $false } | ||||
|         Mock get_config { $false } | ||||
|         Test-Aria2Enabled | Should -BeFalse | ||||
| @@ -96,299 +96,304 @@ describe "Test-Aria2Enabled" -Tag 'Scoop' { | ||||
|     } | ||||
| } | ||||
|  | ||||
| describe "Test-CommandAvailable" -Tag 'Scoop' { | ||||
|     it "should return true if command exists" { | ||||
| Describe 'Test-CommandAvailable' -Tag 'Scoop' { | ||||
|     It 'should return true if command exists' { | ||||
|         Test-CommandAvailable 'Write-Host' | Should -BeTrue | ||||
|     } | ||||
|  | ||||
|     it "should return false if command doesn't exist" { | ||||
|     It "should return false if command doesn't exist" { | ||||
|         Test-CommandAvailable 'Write-ThisWillProbablyNotExist' | Should -BeFalse | ||||
|     } | ||||
|  | ||||
|     it "should throw if parameter is wrong or missing" { | ||||
|     It 'should throw if parameter is wrong or missing' { | ||||
|         { Test-CommandAvailable } | Should -Throw | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| describe "is_directory" -Tag 'Scoop' { | ||||
|     beforeall { | ||||
|         $working_dir = setup_working "is_directory" | ||||
| Describe 'is_directory' -Tag 'Scoop' { | ||||
|     BeforeAll { | ||||
|         $working_dir = setup_working 'is_directory' | ||||
|     } | ||||
|  | ||||
|     it "is_directory recognize directories" { | ||||
|         is_directory "$working_dir\i_am_a_directory" | should -be $true | ||||
|     It 'is_directory recognize directories' { | ||||
|         is_directory "$working_dir\i_am_a_directory" | Should -Be $true | ||||
|     } | ||||
|     it "is_directory recognize files" { | ||||
|         is_directory "$working_dir\i_am_a_file.txt" | should -be $false | ||||
|     It 'is_directory recognize files' { | ||||
|         is_directory "$working_dir\i_am_a_file.txt" | Should -Be $false | ||||
|     } | ||||
|  | ||||
|     it "is_directory is falsey on unknown path" { | ||||
|         is_directory "$working_dir\i_do_not_exist" | should -be $false | ||||
|     It 'is_directory is falsey on unknown path' { | ||||
|         is_directory "$working_dir\i_do_not_exist" | Should -Be $false | ||||
|     } | ||||
| } | ||||
|  | ||||
| describe "movedir" -Tag 'Scoop' { | ||||
|     $extract_dir = "subdir" | ||||
| Describe 'movedir' -Tag 'Scoop' { | ||||
|     $extract_dir = 'subdir' | ||||
|     $extract_to = $null | ||||
|  | ||||
|     beforeall { | ||||
|         $working_dir = setup_working "movedir" | ||||
|     BeforeAll { | ||||
|         $working_dir = setup_working 'movedir' | ||||
|     } | ||||
|  | ||||
|     it "moves directories with no spaces in path" -skip:$isUnix { | ||||
|     It 'moves directories with no spaces in path' -Skip:$isUnix { | ||||
|         $dir = "$working_dir\user" | ||||
|         movedir "$dir\_tmp\$extract_dir" "$dir\$extract_to" | ||||
|  | ||||
|         "$dir\test.txt" | should -FileContentMatch "this is the one" | ||||
|         "$dir\_tmp\$extract_dir" | should -not -exist | ||||
|         "$dir\test.txt" | Should -FileContentMatch 'this is the one' | ||||
|         "$dir\_tmp\$extract_dir" | Should -Not -Exist | ||||
|     } | ||||
|  | ||||
|     it "moves directories with spaces in path" -skip:$isUnix { | ||||
|     It 'moves directories with spaces in path' -Skip:$isUnix { | ||||
|         $dir = "$working_dir\user with space" | ||||
|         movedir "$dir\_tmp\$extract_dir" "$dir\$extract_to" | ||||
|  | ||||
|         "$dir\test.txt" | should -FileContentMatch "this is the one" | ||||
|         "$dir\_tmp\$extract_dir" | should -not -exist | ||||
|         "$dir\test.txt" | Should -FileContentMatch 'this is the one' | ||||
|         "$dir\_tmp\$extract_dir" | Should -Not -Exist | ||||
|  | ||||
|         # test trailing \ in from dir | ||||
|         movedir "$dir\_tmp\$null" "$dir\another" | ||||
|         "$dir\another\test.txt" | should -FileContentMatch "testing" | ||||
|         "$dir\_tmp" | should -not -exist | ||||
|         "$dir\another\test.txt" | Should -FileContentMatch 'testing' | ||||
|         "$dir\_tmp" | Should -Not -Exist | ||||
|     } | ||||
|  | ||||
|     it "moves directories with quotes in path" -skip:$isUnix { | ||||
|     It 'moves directories with quotes in path' -Skip:$isUnix { | ||||
|         $dir = "$working_dir\user with 'quote" | ||||
|         movedir "$dir\_tmp\$extract_dir" "$dir\$extract_to" | ||||
|  | ||||
|         "$dir\test.txt" | should -FileContentMatch "this is the one" | ||||
|         "$dir\_tmp\$extract_dir" | should -not -exist | ||||
|         "$dir\test.txt" | Should -FileContentMatch 'this is the one' | ||||
|         "$dir\_tmp\$extract_dir" | Should -Not -Exist | ||||
|     } | ||||
| } | ||||
|  | ||||
| describe "shim" -Tag 'Scoop' { | ||||
|     beforeall { | ||||
|         $working_dir = setup_working "shim" | ||||
|         $shimdir = shimdir | ||||
|         $(ensure_in_path $shimdir) | out-null | ||||
|     } | ||||
|  | ||||
|     it "links a file onto the user's path" -skip:$isUnix { | ||||
|         { get-command "shim-test" -ea stop } | should -throw | ||||
|         { get-command "shim-test.ps1" -ea stop } | should -throw | ||||
|         { get-command "shim-test.cmd" -ea stop } | should -throw | ||||
|         { shim-test } | should -throw | ||||
|  | ||||
|         shim "$working_dir\shim-test.ps1" $false "shim-test" | ||||
|         { get-command "shim-test" -ea stop } | should -not -throw | ||||
|         { get-command "shim-test.ps1" -ea stop } | should -not -throw | ||||
|         { get-command "shim-test.cmd" -ea stop } | should -not -throw | ||||
|         shim-test | should -be "Hello, world!" | ||||
|     } | ||||
|  | ||||
|     context "user with quote" { | ||||
|         it "shims a file with quote in path" -skip:$isUnix { | ||||
|             { get-command "shim-test" -ea stop } | should -throw | ||||
|             { shim-test } | should -throw | ||||
|  | ||||
|             shim "$working_dir\user with 'quote\shim-test.ps1" $false "shim-test" | ||||
|             { get-command "shim-test" -ea stop } | should -not -throw | ||||
|             shim-test | should -be "Hello, world!" | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     aftereach { | ||||
|         rm_shim "shim-test" $shimdir | ||||
|     } | ||||
| } | ||||
|  | ||||
| describe "rm_shim" -Tag 'Scoop' { | ||||
|     beforeall { | ||||
|         $working_dir = setup_working "shim" | ||||
|         $shimdir = shimdir | ||||
|         $(ensure_in_path $shimdir) | out-null | ||||
|     } | ||||
|  | ||||
|     it "removes shim from path" -skip:$isUnix { | ||||
|         shim "$working_dir\shim-test.ps1" $false "shim-test" | ||||
|  | ||||
|         rm_shim "shim-test" $shimdir | ||||
|  | ||||
|         { get-command "shim-test" -ea stop } | should -throw | ||||
|         { get-command "shim-test.ps1" -ea stop } | should -throw | ||||
|         { get-command "shim-test.cmd" -ea stop } | should -throw | ||||
|         { shim-test } | should -throw | ||||
|     } | ||||
| } | ||||
|  | ||||
| Describe "get_app_name_from_ps1_shim" -Tag 'Scoop' { | ||||
| Describe 'shim' -Tag 'Scoop' { | ||||
|     BeforeAll { | ||||
|         $working_dir = setup_working "shim" | ||||
|         $working_dir = setup_working 'shim' | ||||
|         $shimdir = shimdir | ||||
|         $(ensure_in_path $shimdir) | Out-Null | ||||
|     } | ||||
|  | ||||
|     It "returns empty string if file does not exist" -skip:$isUnix { | ||||
|         get_app_name_from_ps1_shim "non-existent-file" | should -be "" | ||||
|     It "links a file onto the user's path" -Skip:$isUnix { | ||||
|         { Get-Command 'shim-test' -ea stop } | Should -Throw | ||||
|         { Get-Command 'shim-test.ps1' -ea stop } | Should -Throw | ||||
|         { Get-Command 'shim-test.cmd' -ea stop } | Should -Throw | ||||
|         { shim-test } | Should -Throw | ||||
|  | ||||
|         shim "$working_dir\shim-test.ps1" $false 'shim-test' | ||||
|         { Get-Command 'shim-test' -ea stop } | Should -Not -Throw | ||||
|         { Get-Command 'shim-test.ps1' -ea stop } | Should -Not -Throw | ||||
|         { Get-Command 'shim-test.cmd' -ea stop } | Should -Not -Throw | ||||
|         shim-test | Should -Be 'Hello, world!' | ||||
|     } | ||||
|  | ||||
|     It "returns app name if file exists and is a shim to an app" -skip:$isUnix { | ||||
|         mkdir -p "$working_dir/mockapp/current/" | ||||
|         Write-Output "" | Out-File "$working_dir/mockapp/current/mockapp.ps1" | ||||
|         shim "$working_dir/mockapp/current/mockapp.ps1" $false "shim-test" | ||||
|         $shim_path = (get-command "shim-test.ps1").Path | ||||
|         get_app_name_from_ps1_shim "$shim_path" | should -be "mockapp" | ||||
|     } | ||||
|     Context 'user with quote' { | ||||
|         It 'shims a file with quote in path' -Skip:$isUnix { | ||||
|             { Get-Command 'shim-test' -ea stop } | Should -Throw | ||||
|             { shim-test } | Should -Throw | ||||
|  | ||||
|     It "returns empty string if file exists and is not a shim" -skip:$isUnix { | ||||
|         Write-Output "lorem ipsum" | Out-File -Encoding ascii "$working_dir/mock-shim.ps1" | ||||
|         get_app_name_from_ps1_shim "$working_dir/mock-shim.ps1" | should -be "" | ||||
|             shim "$working_dir\user with 'quote\shim-test.ps1" $false 'shim-test' | ||||
|             { Get-Command 'shim-test' -ea stop } | Should -Not -Throw | ||||
|             shim-test | Should -Be 'Hello, world!' | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     AfterEach { | ||||
|         if (Get-Command "shim-test" -ErrorAction SilentlyContinue) { | ||||
|             rm_shim "shim-test" $shimdir -ErrorAction SilentlyContinue | ||||
|         rm_shim 'shim-test' $shimdir | ||||
|     } | ||||
| } | ||||
|  | ||||
| Describe 'rm_shim' -Tag 'Scoop' { | ||||
|     BeforeAll { | ||||
|         $working_dir = setup_working 'shim' | ||||
|         $shimdir = shimdir | ||||
|         $(ensure_in_path $shimdir) | Out-Null | ||||
|     } | ||||
|  | ||||
|     It 'removes shim from path' -Skip:$isUnix { | ||||
|         shim "$working_dir\shim-test.ps1" $false 'shim-test' | ||||
|  | ||||
|         rm_shim 'shim-test' $shimdir | ||||
|  | ||||
|         { Get-Command 'shim-test' -ea stop } | Should -Throw | ||||
|         { Get-Command 'shim-test.ps1' -ea stop } | Should -Throw | ||||
|         { Get-Command 'shim-test.cmd' -ea stop } | Should -Throw | ||||
|         { shim-test } | Should -Throw | ||||
|     } | ||||
| } | ||||
|  | ||||
| Describe 'get_app_name_from_shim' -Tag 'Scoop' { | ||||
|     BeforeAll { | ||||
|         $working_dir = setup_working 'shim' | ||||
|         $shimdir = shimdir | ||||
|         $(ensure_in_path $shimdir) | Out-Null | ||||
|         Mock appsdir { $working_dir } | ||||
|     } | ||||
|  | ||||
|     It 'returns empty string if file does not exist' -Skip:$isUnix { | ||||
|         get_app_name_from_shim 'non-existent-file' | Should -Be '' | ||||
|     } | ||||
|  | ||||
|     It 'returns app name if file exists and is a shim to an app' -Skip:$isUnix { | ||||
|         ensure "$working_dir/mockapp/current/" | ||||
|         Write-Output '' | Out-File "$working_dir/mockapp/current/mockapp1.ps1" | ||||
|         shim "$working_dir/mockapp/current/mockapp1.ps1" $false 'shim-test1' | ||||
|         $shim_path1 = (Get-Command 'shim-test1.ps1').Path | ||||
|         get_app_name_from_shim "$shim_path1" | Should -Be 'mockapp' | ||||
|         ensure "$working_dir/mockapp/1.0.0/" | ||||
|         Write-Output '' | Out-File "$working_dir/mockapp/1.0.0/mockapp2.ps1" | ||||
|         shim "$working_dir/mockapp/1.0.0/mockapp2.ps1" $false 'shim-test2' | ||||
|         $shim_path2 = (Get-Command 'shim-test2.ps1').Path | ||||
|         get_app_name_from_shim "$shim_path2" | Should -Be 'mockapp' | ||||
|     } | ||||
|  | ||||
|     It 'returns empty string if file exists and is not a shim' -Skip:$isUnix { | ||||
|         Write-Output 'lorem ipsum' | Out-File -Encoding ascii "$working_dir/mock-shim.ps1" | ||||
|         get_app_name_from_shim "$working_dir/mock-shim.ps1" | Should -Be '' | ||||
|     } | ||||
|  | ||||
|     AfterAll { | ||||
|         if (Get-Command 'shim-test1' -ErrorAction SilentlyContinue) { | ||||
|             rm_shim 'shim-test1' $shimdir -ErrorAction SilentlyContinue | ||||
|         } | ||||
|         if (Get-Command 'shim-test2' -ErrorAction SilentlyContinue) { | ||||
|             rm_shim 'shim-test2' $shimdir -ErrorAction SilentlyContinue | ||||
|         } | ||||
|         Remove-Item -Force -Recurse -ErrorAction SilentlyContinue "$working_dir/mockapp" | ||||
|         Remove-Item -Force -ErrorAction SilentlyContinue "$working_dir/moch-shim.ps1" | ||||
|     } | ||||
| } | ||||
|  | ||||
| describe "ensure_robocopy_in_path" -Tag 'Scoop' { | ||||
| Describe 'ensure_robocopy_in_path' -Tag 'Scoop' { | ||||
|     $shimdir = shimdir $false | ||||
|     mock versiondir { $repo_dir } | ||||
|     Mock versiondir { $repo_dir } | ||||
|  | ||||
|     beforeall { | ||||
|         reset_aliases | ||||
|     } | ||||
|  | ||||
|     context "robocopy is not in path" { | ||||
|         it "shims robocopy when not on path" -skip:$isUnix { | ||||
|             mock Test-CommandAvailable { $false } | ||||
|             Test-CommandAvailable robocopy | should -be $false | ||||
|     Context 'robocopy is not in path' { | ||||
|         It 'shims robocopy when not on path' -Skip:$isUnix { | ||||
|             Mock Test-CommandAvailable { $false } | ||||
|             Test-CommandAvailable robocopy | Should -Be $false | ||||
|  | ||||
|             ensure_robocopy_in_path | ||||
|  | ||||
|             "$shimdir/robocopy.ps1" | should -exist | ||||
|             "$shimdir/robocopy.exe" | should -exist | ||||
|             # "$shimdir/robocopy.ps1" | should -exist | ||||
|             "$shimdir/robocopy.exe" | Should -Exist | ||||
|  | ||||
|             # clean up | ||||
|             rm_shim robocopy $(shimdir $false) | out-null | ||||
|             rm_shim robocopy $(shimdir $false) | Out-Null | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     context "robocopy is in path" { | ||||
|         it "does not shim robocopy when it is in path" -skip:$isUnix { | ||||
|             mock Test-CommandAvailable { $true } | ||||
|             Test-CommandAvailable robocopy | should -be $true | ||||
|     Context 'robocopy is in path' { | ||||
|         It 'does not shim robocopy when it is in path' -Skip:$isUnix { | ||||
|             Mock Test-CommandAvailable { $true } | ||||
|             Test-CommandAvailable robocopy | Should -Be $true | ||||
|  | ||||
|             ensure_robocopy_in_path | ||||
|  | ||||
|             "$shimdir/robocopy.ps1" | should -not -exist | ||||
|             "$shimdir/robocopy.exe" | should -not -exist | ||||
|             # "$shimdir/robocopy.ps1" | should -not -exist | ||||
|             "$shimdir/robocopy.exe" | Should -Not -Exist | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| describe 'sanitary_path' -Tag 'Scoop' { | ||||
|   it 'removes invalid path characters from a string' { | ||||
|     $path = 'test?.json' | ||||
|     $valid_path = sanitary_path $path | ||||
| Describe 'sanitary_path' -Tag 'Scoop' { | ||||
|     It 'removes invalid path characters from a string' { | ||||
|         $path = 'test?.json' | ||||
|         $valid_path = sanitary_path $path | ||||
|  | ||||
|     $valid_path | should -be "test.json" | ||||
|   } | ||||
| } | ||||
|  | ||||
| describe 'app' -Tag 'Scoop' { | ||||
|     it 'parses the bucket name from an app query' { | ||||
|         $query = "C:\test.json" | ||||
|         $app, $bucket, $version = parse_app $query | ||||
|         $app | should -be "C:\test.json" | ||||
|         $bucket | should -benullorempty | ||||
|         $version | should -benullorempty | ||||
|  | ||||
|         $query = "test.json" | ||||
|         $app, $bucket, $version = parse_app $query | ||||
|         $app | should -be "test.json" | ||||
|         $bucket | should -benullorempty | ||||
|         $version | should -benullorempty | ||||
|  | ||||
|         $query = ".\test.json" | ||||
|         $app, $bucket, $version = parse_app $query | ||||
|         $app | should -be ".\test.json" | ||||
|         $bucket | should -benullorempty | ||||
|         $version | should -benullorempty | ||||
|  | ||||
|         $query = "..\test.json" | ||||
|         $app, $bucket, $version = parse_app $query | ||||
|         $app | should -be "..\test.json" | ||||
|         $bucket | should -benullorempty | ||||
|         $version | should -benullorempty | ||||
|  | ||||
|         $query = "\\share\test.json" | ||||
|         $app, $bucket, $version = parse_app $query | ||||
|         $app | should -be "\\share\test.json" | ||||
|         $bucket | should -benullorempty | ||||
|         $version | should -benullorempty | ||||
|  | ||||
|         $query = "https://example.com/test.json" | ||||
|         $app, $bucket, $version = parse_app $query | ||||
|         $app | should -be "https://example.com/test.json" | ||||
|         $bucket | should -benullorempty | ||||
|         $version | should -benullorempty | ||||
|  | ||||
|         $query = "test" | ||||
|         $app, $bucket, $version = parse_app $query | ||||
|         $app | should -be "test" | ||||
|         $bucket | should -benullorempty | ||||
|         $version | should -benullorempty | ||||
|  | ||||
|         $query = "extras/enso" | ||||
|         $app, $bucket, $version = parse_app $query | ||||
|         $app | should -be "enso" | ||||
|         $bucket | should -be "extras" | ||||
|         $version | should -benullorempty | ||||
|  | ||||
|         $query = "test-app" | ||||
|         $app, $bucket, $version = parse_app $query | ||||
|         $app | should -be "test-app" | ||||
|         $bucket | should -benullorempty | ||||
|         $version | should -benullorempty | ||||
|  | ||||
|         $query = "test-bucket/test-app" | ||||
|         $app, $bucket, $version = parse_app $query | ||||
|         $app | should -be "test-app" | ||||
|         $bucket | should -be "test-bucket" | ||||
|         $version | should -benullorempty | ||||
|  | ||||
|         $query = "test-bucket/test-app@1.8.0" | ||||
|         $app, $bucket, $version = parse_app $query | ||||
|         $app | should -be "test-app" | ||||
|         $bucket | should -be "test-bucket" | ||||
|         $version | should -be "1.8.0" | ||||
|  | ||||
|         $query = "test-bucket/test-app@1.8.0-rc2" | ||||
|         $app, $bucket, $version = parse_app $query | ||||
|         $app | should -be "test-app" | ||||
|         $bucket | should -be "test-bucket" | ||||
|         $version | should -be "1.8.0-rc2" | ||||
|  | ||||
|         $query = "test-bucket/test_app" | ||||
|         $app, $bucket, $version = parse_app $query | ||||
|         $app | should -be "test_app" | ||||
|         $bucket | should -be "test-bucket" | ||||
|         $version | should -benullorempty | ||||
|  | ||||
|         $query = "test-bucket/test_app@1.8.0" | ||||
|         $app, $bucket, $version = parse_app $query | ||||
|         $app | should -be "test_app" | ||||
|         $bucket | should -be "test-bucket" | ||||
|         $version | should -be "1.8.0" | ||||
|  | ||||
|         $query = "test-bucket/test_app@1.8.0-rc2" | ||||
|         $app, $bucket, $version = parse_app $query | ||||
|         $app | should -be "test_app" | ||||
|         $bucket | should -be "test-bucket" | ||||
|         $version | should -be "1.8.0-rc2" | ||||
|         $valid_path | Should -Be 'test.json' | ||||
|     } | ||||
| } | ||||
|  | ||||
| Describe 'app' -Tag 'Scoop' { | ||||
|     It 'parses the bucket name from an app query' { | ||||
|         $query = 'C:\test.json' | ||||
|         $app, $bucket, $version = parse_app $query | ||||
|         $app | Should -Be 'C:\test.json' | ||||
|         $bucket | Should -BeNullOrEmpty | ||||
|         $version | Should -BeNullOrEmpty | ||||
|  | ||||
|         $query = 'test.json' | ||||
|         $app, $bucket, $version = parse_app $query | ||||
|         $app | Should -Be 'test.json' | ||||
|         $bucket | Should -BeNullOrEmpty | ||||
|         $version | Should -BeNullOrEmpty | ||||
|  | ||||
|         $query = '.\test.json' | ||||
|         $app, $bucket, $version = parse_app $query | ||||
|         $app | Should -Be '.\test.json' | ||||
|         $bucket | Should -BeNullOrEmpty | ||||
|         $version | Should -BeNullOrEmpty | ||||
|  | ||||
|         $query = '..\test.json' | ||||
|         $app, $bucket, $version = parse_app $query | ||||
|         $app | Should -Be '..\test.json' | ||||
|         $bucket | Should -BeNullOrEmpty | ||||
|         $version | Should -BeNullOrEmpty | ||||
|  | ||||
|         $query = '\\share\test.json' | ||||
|         $app, $bucket, $version = parse_app $query | ||||
|         $app | Should -Be '\\share\test.json' | ||||
|         $bucket | Should -BeNullOrEmpty | ||||
|         $version | Should -BeNullOrEmpty | ||||
|  | ||||
|         $query = 'https://example.com/test.json' | ||||
|         $app, $bucket, $version = parse_app $query | ||||
|         $app | Should -Be 'https://example.com/test.json' | ||||
|         $bucket | Should -BeNullOrEmpty | ||||
|         $version | Should -BeNullOrEmpty | ||||
|  | ||||
|         $query = 'test' | ||||
|         $app, $bucket, $version = parse_app $query | ||||
|         $app | Should -Be 'test' | ||||
|         $bucket | Should -BeNullOrEmpty | ||||
|         $version | Should -BeNullOrEmpty | ||||
|  | ||||
|         $query = 'extras/enso' | ||||
|         $app, $bucket, $version = parse_app $query | ||||
|         $app | Should -Be 'enso' | ||||
|         $bucket | Should -Be 'extras' | ||||
|         $version | Should -BeNullOrEmpty | ||||
|  | ||||
|         $query = 'test-app' | ||||
|         $app, $bucket, $version = parse_app $query | ||||
|         $app | Should -Be 'test-app' | ||||
|         $bucket | Should -BeNullOrEmpty | ||||
|         $version | Should -BeNullOrEmpty | ||||
|  | ||||
|         $query = 'test-bucket/test-app' | ||||
|         $app, $bucket, $version = parse_app $query | ||||
|         $app | Should -Be 'test-app' | ||||
|         $bucket | Should -Be 'test-bucket' | ||||
|         $version | Should -BeNullOrEmpty | ||||
|  | ||||
|         $query = 'test-bucket/test-app@1.8.0' | ||||
|         $app, $bucket, $version = parse_app $query | ||||
|         $app | Should -Be 'test-app' | ||||
|         $bucket | Should -Be 'test-bucket' | ||||
|         $version | Should -Be '1.8.0' | ||||
|  | ||||
|         $query = 'test-bucket/test-app@1.8.0-rc2' | ||||
|         $app, $bucket, $version = parse_app $query | ||||
|         $app | Should -Be 'test-app' | ||||
|         $bucket | Should -Be 'test-bucket' | ||||
|         $version | Should -Be '1.8.0-rc2' | ||||
|  | ||||
|         $query = 'test-bucket/test_app' | ||||
|         $app, $bucket, $version = parse_app $query | ||||
|         $app | Should -Be 'test_app' | ||||
|         $bucket | Should -Be 'test-bucket' | ||||
|         $version | Should -BeNullOrEmpty | ||||
|  | ||||
|         $query = 'test-bucket/test_app@1.8.0' | ||||
|         $app, $bucket, $version = parse_app $query | ||||
|         $app | Should -Be 'test_app' | ||||
|         $bucket | Should -Be 'test-bucket' | ||||
|         $version | Should -Be '1.8.0' | ||||
|  | ||||
|         $query = 'test-bucket/test_app@1.8.0-rc2' | ||||
|         $app, $bucket, $version = parse_app $query | ||||
|         $app | Should -Be 'test_app' | ||||
|         $bucket | Should -Be 'test-bucket' | ||||
|         $version | Should -Be '1.8.0-rc2' | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,37 +1,40 @@ | ||||
| . "$psscriptroot\Scoop-TestLib.ps1" | ||||
| . "$psscriptroot\..\lib\decompress.ps1" | ||||
| . "$psscriptroot\..\lib\unix.ps1" | ||||
| . "$psscriptroot\..\lib\install.ps1" | ||||
| . "$psscriptroot\..\lib\manifest.ps1" | ||||
| . "$PSScriptRoot\Scoop-TestLib.ps1" | ||||
| . "$PSScriptRoot\..\lib\core.ps1" | ||||
| . "$PSScriptRoot\..\lib\decompress.ps1" | ||||
| . "$PSScriptRoot\..\lib\install.ps1" | ||||
| . "$PSScriptRoot\..\lib\manifest.ps1" | ||||
| . "$PSScriptRoot\..\lib\versions.ps1" | ||||
| . "$PSScriptRoot\..\lib\unix.ps1" | ||||
|  | ||||
| $isUnix = is_unix | ||||
|  | ||||
| function test_extract($extract_fn, $from, $removal) { | ||||
|     $to = (strip_ext $from) -replace '\.tar$', '' | ||||
|     & $extract_fn ($from -replace '/', '\') ($to -replace '/', '\') -Removal:$removal | ||||
|     return $to | ||||
| } | ||||
|  | ||||
| Describe 'Decompression function' -Tag 'Scoop', 'Decompress' { | ||||
|  | ||||
|     BeforeAll { | ||||
|         $working_dir = setup_working 'decompress' | ||||
|  | ||||
|         It "Decompression test cases should exist" { | ||||
|         function test_extract($extract_fn, $from, $removal) { | ||||
|             $to = (strip_ext $from) -replace '\.tar$', '' | ||||
|             & $extract_fn ($from -replace '/', '\') ($to -replace '/', '\') -Removal:$removal | ||||
|             return $to | ||||
|         } | ||||
|  | ||||
|         It 'Decompression test cases should exist' { | ||||
|             $testcases = "$working_dir\TestCases.zip" | ||||
|             $testcases | Should -Exist | ||||
|             compute_hash $testcases 'sha256' | Should -Be '695bb18cafda52644a19afd184b2545e9c48f1a191f7ff1efc26cb034587079c' | ||||
|             compute_hash $testcases 'sha256' | Should -Be '3a442e85b466833eeafbd08c57d8f51bf7ff041867ee0bdb7db1f12480b3624a' | ||||
|             if (!$isUnix) { | ||||
|                 Microsoft.Powershell.Archive\Expand-Archive $testcases $working_dir | ||||
|                 Microsoft.PowerShell.Archive\Expand-Archive $testcases $working_dir | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Context "7zip extraction" { | ||||
|     Context '7zip extraction' { | ||||
|  | ||||
|         BeforeAll { | ||||
|             if($env:CI) { | ||||
|                 mock Get-AppFilePath { (Get-Command 7z.exe).Path } | ||||
|             } elseif(!(installed 7zip)) { | ||||
|             if ($env:CI) { | ||||
|                 Mock Get-AppFilePath { (Get-Command 7z.exe).Path } | ||||
|             } elseif (!(installed 7zip)) { | ||||
|                 scoop install 7zip | ||||
|             } | ||||
|             $test1 = "$working_dir\7ZipTest1.7z" | ||||
| @@ -40,116 +43,151 @@ Describe 'Decompression function' -Tag 'Scoop', 'Decompress' { | ||||
|             $test4 = "$working_dir\7ZipTest4.tar.gz" | ||||
|         } | ||||
|  | ||||
|         It "extract normal compressed file" -Skip:$isUnix { | ||||
|             $to = test_extract "Expand-7zipArchive" $test1 | ||||
|         It 'extract normal compressed file' -Skip:$isUnix { | ||||
|             $to = test_extract 'Expand-7zipArchive' $test1 | ||||
|             $to | Should -Exist | ||||
|             "$to\empty" | Should -Exist | ||||
|             (Get-ChildItem $to).Count | Should -Be 1 | ||||
|         } | ||||
|  | ||||
|         It "extract nested compressed file" -Skip:$isUnix { | ||||
|         It 'extract nested compressed file' -Skip:$isUnix { | ||||
|             # file ext: tgz | ||||
|             $to = test_extract "Expand-7zipArchive" $test2 | ||||
|             $to = test_extract 'Expand-7zipArchive' $test2 | ||||
|             $to | Should -Exist | ||||
|             "$to\empty" | Should -Exist | ||||
|             (Get-ChildItem $to).Count | Should -Be 1 | ||||
|  | ||||
|             # file ext: tar.bz2 | ||||
|             $to = test_extract "Expand-7zipArchive" $test3 | ||||
|             $to = test_extract 'Expand-7zipArchive' $test3 | ||||
|             $to | Should -Exist | ||||
|             "$to\empty" | Should -Exist | ||||
|             (Get-ChildItem $to).Count | Should -Be 1 | ||||
|         } | ||||
|  | ||||
|         It "extract nested compressed file with different inner name" -Skip:$isUnix { | ||||
|             $to = test_extract "Expand-7zipArchive" $test4 | ||||
|         It 'extract nested compressed file with different inner name' -Skip:$isUnix { | ||||
|             $to = test_extract 'Expand-7zipArchive' $test4 | ||||
|             $to | Should -Exist | ||||
|             "$to\empty" | Should -Exist | ||||
|             (Get-ChildItem $to).Count | Should -Be 1 | ||||
|         } | ||||
|  | ||||
|         It "works with '-Removal' switch (`$removal param)" -Skip:$isUnix { | ||||
|         It 'works with "-Removal" switch ($removal param)' -Skip:$isUnix { | ||||
|             $test1 | Should -Exist | ||||
|             test_extract "Expand-7zipArchive" $test1 $true | ||||
|             test_extract 'Expand-7zipArchive' $test1 $true | ||||
|             $test1 | Should -Not -Exist | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Context "msi extraction" { | ||||
|     Context 'zstd extraction' { | ||||
|  | ||||
|         BeforeAll { | ||||
|             if($env:CI) { | ||||
|                 mock Get-AppFilePath { $env:lessmsi } | ||||
|             } elseif(!(installed lessmsi)) { | ||||
|             if ($env:CI) { | ||||
|                 Mock Get-AppFilePath { $env:SCOOP_ZSTD_PATH } -ParameterFilter { $Helper -eq 'zstd' } | ||||
|                 Mock Get-AppFilePath { '7z.exe' } -ParameterFilter { $Helper -eq '7zip' } | ||||
|             } elseif (!(installed zstd)) { | ||||
|                 scoop install zstd | ||||
|             } | ||||
|  | ||||
|             $test1 = "$working_dir\ZstdTest.zst" | ||||
|             $test2 = "$working_dir\ZstdTest.tar.zst" | ||||
|         } | ||||
|  | ||||
|         It 'extract normal compressed file' -Skip:$isUnix { | ||||
|             $to = test_extract 'Expand-ZstdArchive' $test1 | ||||
|             $to | Should -Exist | ||||
|             "$to\ZstdTest" | Should -Exist | ||||
|             (Get-ChildItem $to).Count | Should -Be 1 | ||||
|         } | ||||
|  | ||||
|         It 'extract nested compressed file' -Skip:$isUnix { | ||||
|             $to = test_extract 'Expand-ZstdArchive' $test2 | ||||
|             $to | Should -Exist | ||||
|             "$to\ZstdTest" | Should -Exist | ||||
|             (Get-ChildItem $to).Count | Should -Be 1 | ||||
|         } | ||||
|  | ||||
|         It 'works with "-Removal" switch ($removal param)' -Skip:$isUnix { | ||||
|             $test1 | Should -Exist | ||||
|             test_extract 'Expand-ZstdArchive' $test1 $true | ||||
|             $test1 | Should -Not -Exist | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Context 'msi extraction' { | ||||
|  | ||||
|         BeforeAll { | ||||
|             if ($env:CI) { | ||||
|                 Mock Get-AppFilePath { $env:SCOOP_LESSMSI_PATH } | ||||
|             } elseif (!(installed lessmsi)) { | ||||
|                 scoop install lessmsi | ||||
|             } | ||||
|             $test1 = "$working_dir\MSITest.msi" | ||||
|             $test2 = "$working_dir\MSITestNull.msi" | ||||
|         } | ||||
|  | ||||
|         It "extract normal MSI file" -Skip:$isUnix { | ||||
|             mock get_config { $false } | ||||
|             $to = test_extract "Expand-MsiArchive" $test1 | ||||
|         It 'extract normal MSI file' -Skip:$isUnix { | ||||
|             Mock get_config { $false } | ||||
|             $to = test_extract 'Expand-MsiArchive' $test1 | ||||
|             $to | Should -Exist | ||||
|             "$to\MSITest\empty" | Should -Exist | ||||
|             (Get-ChildItem "$to\MSITest").Count | Should -Be 1 | ||||
|         } | ||||
|  | ||||
|         It "extract empty MSI file using lessmsi" -Skip:$isUnix { | ||||
|             mock get_config { $true } | ||||
|             $to = test_extract "Expand-MsiArchive" $test2 | ||||
|         It 'extract empty MSI file using lessmsi' -Skip:$isUnix { | ||||
|             Mock get_config { $true } | ||||
|             $to = test_extract 'Expand-MsiArchive' $test2 | ||||
|             $to | Should -Exist | ||||
|         } | ||||
|  | ||||
|         It "works with '-Removal' switch (`$removal param)" -Skip:$isUnix { | ||||
|             mock get_config { $false } | ||||
|         It 'works with "-Removal" switch ($removal param)' -Skip:$isUnix { | ||||
|             Mock get_config { $false } | ||||
|             $test1 | Should -Exist | ||||
|             test_extract "Expand-MsiArchive" $test1 $true | ||||
|             test_extract 'Expand-MsiArchive' $test1 $true | ||||
|             $test1 | Should -Not -Exist | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Context "inno extraction" { | ||||
|     Context 'inno extraction' { | ||||
|  | ||||
|         BeforeAll { | ||||
|             if($env:CI) { | ||||
|                 mock Get-AppFilePath { $env:innounp } | ||||
|             } elseif(!(installed innounp)) { | ||||
|             if ($env:CI) { | ||||
|                 Mock Get-AppFilePath { $env:SCOOP_INNOUNP_PATH } | ||||
|             } elseif (!(installed innounp)) { | ||||
|                 scoop install innounp | ||||
|             } | ||||
|             $test = "$working_dir\InnoTest.exe" | ||||
|         } | ||||
|  | ||||
|         It "extract Inno Setup file" -Skip:$isUnix { | ||||
|             $to = test_extract "Expand-InnoArchive" $test | ||||
|         It 'extract Inno Setup file' -Skip:$isUnix { | ||||
|             $to = test_extract 'Expand-InnoArchive' $test | ||||
|             $to | Should -Exist | ||||
|             "$to\empty" | Should -Exist | ||||
|             (Get-ChildItem $to).Count | Should -Be 1 | ||||
|         } | ||||
|  | ||||
|         It "works with '-Removal' switch (`$removal param)" -Skip:$isUnix { | ||||
|         It 'works with "-Removal" switch ($removal param)' -Skip:$isUnix { | ||||
|             $test | Should -Exist | ||||
|             test_extract "Expand-InnoArchive" $test $true | ||||
|             test_extract 'Expand-InnoArchive' $test $true | ||||
|             $test | Should -Not -Exist | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Context "zip extraction" { | ||||
|     Context 'zip extraction' { | ||||
|  | ||||
|         BeforeAll { | ||||
|             $test = "$working_dir\ZipTest.zip" | ||||
|         } | ||||
|  | ||||
|         It "extract compressed file" -Skip:$isUnix { | ||||
|             $to = test_extract "Expand-ZipArchive" $test | ||||
|         It 'extract compressed file' -Skip:$isUnix { | ||||
|             $to = test_extract 'Expand-ZipArchive' $test | ||||
|             $to | Should -Exist | ||||
|             "$to\empty" | Should -Exist | ||||
|             (Get-ChildItem $to).Count | Should -Be 1 | ||||
|         } | ||||
|  | ||||
|         It "works with '-Removal' switch (`$removal param)" -Skip:$isUnix { | ||||
|         It 'works with "-Removal" switch ($removal param)' -Skip:$isUnix { | ||||
|             $test | Should -Exist | ||||
|             test_extract "Expand-ZipArchive" $test $true | ||||
|             test_extract 'Expand-ZipArchive' $test $true | ||||
|             $test | Should -Not -Exist | ||||
|         } | ||||
|     } | ||||
|   | ||||
							
								
								
									
										98
									
								
								test/Scoop-Depends.Tests.ps1
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								test/Scoop-Depends.Tests.ps1
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,98 @@ | ||||
| . "$PSScriptRoot\Scoop-TestLib.ps1" | ||||
| . "$PSScriptRoot\..\lib\core.ps1" | ||||
| . "$PSScriptRoot\..\lib\depends.ps1" | ||||
| . "$PSScriptRoot\..\lib\buckets.ps1" | ||||
| . "$PSScriptRoot\..\lib\install.ps1" | ||||
| . "$PSScriptRoot\..\lib\manifest.ps1" | ||||
|  | ||||
| Describe 'Package Dependencies' -Tag 'Scoop' { | ||||
|     Context 'Requirement function' { | ||||
|         It 'Test 7zip requirement' { | ||||
|             Test-7zipRequirement -Uri 'test.xz' | Should -BeTrue | ||||
|             Test-7zipRequirement -Uri 'test.bin' | Should -BeFalse | ||||
|             Test-7zipRequirement -Uri @('test.xz', 'test.bin') | Should -BeTrue | ||||
|         } | ||||
|         It 'Test Zstd requirement' { | ||||
|             Test-ZstdRequirement -Uri 'test.zst' | Should -BeTrue | ||||
|             Test-ZstdRequirement -Uri 'test.bin' | Should -BeFalse | ||||
|             Test-ZstdRequirement -Uri @('test.zst', 'test.bin') | Should -BeTrue | ||||
|         } | ||||
|         It 'Test lessmsi requirement' { | ||||
|             Mock get_config { $true } | ||||
|             Test-LessmsiRequirement -Uri 'test.msi' | Should -BeTrue | ||||
|             Test-LessmsiRequirement -Uri 'test.bin' | Should -BeFalse | ||||
|             Test-LessmsiRequirement -Uri @('test.msi', 'test.bin') | Should -BeTrue | ||||
|         } | ||||
|         It 'Allow $Uri be $null' { | ||||
|             Test-7zipRequirement -Uri $null | Should -BeFalse | ||||
|             Test-ZstdRequirement -Uri $null | Should -BeFalse | ||||
|             Test-LessmsiRequirement -Uri $null | Should -BeFalse | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Context 'InstallationHelper function' { | ||||
|         BeforeAll { | ||||
|             $working_dir = setup_working 'format/formated' | ||||
|             $manifest1 = parse_json (Join-Path $working_dir '3-array-with-single-and-multi.json') | ||||
|             $manifest2 = parse_json (Join-Path $working_dir '4-script-block.json') | ||||
|             Mock Test-HelperInstalled { $false } | ||||
|         } | ||||
|         It 'Get helpers from URL' { | ||||
|             Mock get_config { $true } | ||||
|             Get-InstallationHelper -Manifest $manifest1 -Architecture '32bit' | Should -Be @('lessmsi') | ||||
|         } | ||||
|         It 'Get helpers from script' { | ||||
|             Mock get_config { $false } | ||||
|             Get-InstallationHelper -Manifest $manifest2 -Architecture '32bit' | Should -Be @('7zip') | ||||
|         } | ||||
|         It 'Helpers reflect config changes' { | ||||
|             Mock get_config { $false } -ParameterFilter { $name -eq 'MSIEXTRACT_USE_LESSMSI' } | ||||
|             Mock get_config { $true } -ParameterFilter { $name -eq '7ZIPEXTRACT_USE_EXTERNAL' } | ||||
|             Get-InstallationHelper -Manifest $manifest1 -Architecture '32bit' | Should -BeNullOrEmpty | ||||
|             Get-InstallationHelper -Manifest $manifest2 -Architecture '32bit' | Should -BeNullOrEmpty | ||||
|         } | ||||
|         It 'Not return installed helpers' { | ||||
|             Mock get_config { $true } -ParameterFilter { $name -eq 'MSIEXTRACT_USE_LESSMSI' } | ||||
|             Mock get_config { $false } -ParameterFilter { $name -eq '7ZIPEXTRACT_USE_EXTERNAL' } | ||||
|             Mock Test-HelperInstalled { $true }-ParameterFilter { $Helper -eq '7zip' } | ||||
|             Mock Test-HelperInstalled { $false }-ParameterFilter { $Helper -eq 'Lessmsi' } | ||||
|             Get-InstallationHelper -Manifest $manifest1 -Architecture '32bit' | Should -Be @('lessmsi') | ||||
|             Get-InstallationHelper -Manifest $manifest2 -Architecture '32bit' | Should -BeNullOrEmpty | ||||
|             Mock Test-HelperInstalled { $false }-ParameterFilter { $Helper -eq '7zip' } | ||||
|             Mock Test-HelperInstalled { $true }-ParameterFilter { $Helper -eq 'Lessmsi' } | ||||
|             Get-InstallationHelper -Manifest $manifest1 -Architecture '32bit' | Should -BeNullOrEmpty | ||||
|             Get-InstallationHelper -Manifest $manifest2 -Architecture '32bit' | Should -Be @('7zip') | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Context 'Dependencies resolution' { | ||||
|         BeforeAll { | ||||
|             Mock Test-HelperInstalled { $false } | ||||
|             Mock get_config { $true } -ParameterFilter { $name -eq 'MSIEXTRACT_USE_LESSMSI' } | ||||
|             Mock Find-Manifest { $null, @{}, $null, $null } -ParameterFilter { $AppName -eq 'lessmsi' } | ||||
|             Mock Find-Manifest { $null, @{ url = 'test.msi' }, $null, $null } -ParameterFilter { $AppName -eq '7zip' } | ||||
|             Mock Find-Manifest { $null, @{}, $null, $null } -ParameterFilter { $AppName -eq 'innounp' } | ||||
|         } | ||||
|  | ||||
|         It 'Resolve install dependencies' { | ||||
|             Mock Find-Manifest { $null, @{ url = 'test.7z' }, $null, $null } | ||||
|             Get-Dependency -AppName 'test' -Architecture '32bit' | Should -Be @('lessmsi', '7zip', 'test') | ||||
|             Mock Find-Manifest { $null, @{ innosetup = $true }, $null, $null } | ||||
|             Get-Dependency -AppName 'test' -Architecture '32bit' | Should -Be @('innounp', 'test') | ||||
|         } | ||||
|         It 'Resolve script dependencies' { | ||||
|             Mock Find-Manifest { $null, @{ pre_install = 'Expand-7zipArchive ' }, $null, $null } | ||||
|             Get-Dependency -AppName 'test' -Architecture '32bit' | Should -Be @('lessmsi', '7zip', 'test') | ||||
|         } | ||||
|         It 'Resolve runtime dependencies' { | ||||
|             Mock Find-Manifest { $null, @{}, $null, $null } -ParameterFilter { $AppName -eq 'depends' } | ||||
|             Mock Find-Manifest { $null, @{ depends = 'depends' }, $null, $null } | ||||
|             Get-Dependency -AppName 'test' -Architecture '32bit' | Should -Be @('depends', 'test') | ||||
|         } | ||||
|         It 'Keep bucket name of app' { | ||||
|             Mock Find-Manifest { $null, @{}, $null, $null } -ParameterFilter { $AppName -eq 'bucket/depends' } | ||||
|             Mock Find-Manifest { $null, @{ depends = 'bucket/depends' }, $null, $null } | ||||
|             Get-Dependency -AppName 'anotherbucket/test' -Architecture '32bit' | Should -Be @('bucket/depends', 'anotherbucket/test') | ||||
|         } | ||||
|     } | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user