154 Commits

Author SHA1 Message Date
z-Fng
b592b38abd feat(install): Add separator at the end of notes, highlight suggestions (#6418) 2025-09-24 16:52:03 +08:00
z-Fng
25d35007c6 fix(core|manifest): Avoid error when searching non-existent 'deprecated' directory (#6471) 2025-09-24 14:45:01 +08:00
z-Fng
04b7ce79c7 fix(scoop-version): Fix logic error caused by missing brackets (#6463) 2025-08-27 17:46:16 +08:00
z-Fng
ff38652e01 fix(buckets|scoop-info): Switch git log date format to ISO 8601 to avoid locale issues (#6446) 2025-08-21 14:18:33 +08:00
HUMORCE
c9f0728ff7 fix(autoupdate): Use origin url to handle urls with fragment in github mode (#6455) 2025-08-20 13:59:47 +08:00
AkariiinMKII
691c23330c fix(scoop-download): Fix function nightly_version not defined error (#6386) 2025-08-14 17:51:19 +08:00
Hsiao-nan Cheung
b588a06e41 chore(release): Bump to version 0.5.3 (resync) (#6436) 2025-08-12 18:18:31 +08:00
Hsiao-nan Cheung
9246250808 Merge branch 'master' into develop 2025-08-12 15:21:48 +08:00
Hsiao-nan Cheung
8c51a50856 fix(autoupdate): Check hash file before use GitHub SHA256 (#6435) 2025-08-12 14:36:30 +08:00
Hsiao-nan Cheung
ebd8c036fa chore(release): Bump to version 0.5.3 (#6257)
* fix (decompress): `Expand-7zipArchive` only delete temp dir / `$extractDir` if it is empty (#6092)

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

* refactor(download): Move download-related functions to 'download.ps1' (#6095)

* fix(download): Fallback to default downloader when aria2 fails (#4292)

* fix(commands): Handling broken aliases (#6141)

* fix(shim): properly check `wslpath`/`cygpath` command first (#6114)

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

* fix(scoop-bucket): Add missing import for `no_junction` envs (#6181)

Signed-off-by: Chawye Hsu <su+git@chawyehsu.com>

* docs(chglog): Update to 0.5.3 (#6258)

* perf(shim): Update kiennq-shim to v3.1.2 (#6261)

* fix(decompress): Replace deprecated 7ZIPEXTRACT_USE_EXTERNAL config (#6327)

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

* fix(scoop-uninstall): Fix uninstaller does not gain Global state (#6430)

* global arg

* changelog

* refactor(Get-Manifest): Select actual source for manifest (#6142)

* first step

* Revert "first step"

This reverts commit c5907c3e25.

* refactor(Get-Manifest): Select actual source for installed manifest

* rework sub-commands, `scoop-depends` is NOT working at this stage

* URI manifest

* opt

* deprecated manifest

* source of manifests

* source of manifest pt2

- Mark URI(path/URL/UNC/etc.) query as standalone manifest
- Drop `installed` and `available update` items for [query] and [installed] are different sources.

* remove variable preventing I forget it

* scoop-info: fix source of manifest on bucket

* fix `scoop-depends`

* Fix Standalone and Source detection

* fix global install

* Fix scoop-cat, scoop-home

- Query for remote manifest

* scoop-list: info +deprecated

* manifest: Fix first selected manifest

* gramma..

* Fix 61b3259

* length

* fix(scoop-depends-tests): Mocking `USE_EXTERNAL_7ZIP` as $false (#6431)

* fix(scoop-depends-tests): Mocking `USE_EXTERNAL_7ZIP` as $false to avoding error when it is $true

* CHANGELOG

* feat(autoupdate): GitHub predefined hashes support (#6416)

* feat(autoupdate): predefined hash case for GitHub

- Remove `sha256:` prefix in `format_hash()`
- Add GitHub support in `get_hash_for_app()`

Close #6381

* doc(chglog): GitHub auto hash update

* fix(autoupdate): remove prefix only

* docs(CHANGELOG): Update to 0.5.3 (#6432)

* docs(CHANGELOG): Update to 0.5.3

* 6416

---------

Signed-off-by: Chawye Hsu <su+git@chawyehsu.com>
Co-authored-by: Olav Rønnestad Birkeland <6450056+o-l-a-v@users.noreply.github.com>
Co-authored-by: kiennq <kien.n.quang@gmail.com>
Co-authored-by: HUMORCE <humorce@outlook.com>
Co-authored-by: Ryan <sitiom@proton.me>
Co-authored-by: Chawye Hsu <su+git@chawyehsu.com>
Co-authored-by: Bassel Rachid <101208715+basselworkforce@users.noreply.github.com>
Co-authored-by: Wordless Echo <wordless@echo.moe>
2025-08-11 00:38:38 +00:00
HUMORCE
ed6101a2ab docs(CHANGELOG): Update to 0.5.3 (#6432)
* docs(CHANGELOG): Update to 0.5.3

* 6416
2025-08-11 00:34:50 +00:00
Wordless Echo
26a03e2404 feat(autoupdate): GitHub predefined hashes support (#6416)
* feat(autoupdate): predefined hash case for GitHub

- Remove `sha256:` prefix in `format_hash()`
- Add GitHub support in `get_hash_for_app()`

Close #6381

* doc(chglog): GitHub auto hash update

* fix(autoupdate): remove prefix only
2025-08-11 00:11:46 +00:00
HUMORCE
f9c4f9e175 fix(scoop-depends-tests): Mocking USE_EXTERNAL_7ZIP as $false (#6431)
* fix(scoop-depends-tests): Mocking `USE_EXTERNAL_7ZIP` as $false to avoding error when it is $true

* CHANGELOG
2025-08-10 23:49:22 +00:00
HUMORCE
3321b969a4 refactor(Get-Manifest): Select actual source for manifest (#6142)
* first step

* Revert "first step"

This reverts commit c5907c3e25.

* refactor(Get-Manifest): Select actual source for installed manifest

* rework sub-commands, `scoop-depends` is NOT working at this stage

* URI manifest

* opt

* deprecated manifest

* source of manifests

* source of manifest pt2

- Mark URI(path/URL/UNC/etc.) query as standalone manifest
- Drop `installed` and `available update` items for [query] and [installed] are different sources.

* remove variable preventing I forget it

* scoop-info: fix source of manifest on bucket

* fix `scoop-depends`

* Fix Standalone and Source detection

* fix global install

* Fix scoop-cat, scoop-home

- Query for remote manifest

* scoop-list: info +deprecated

* manifest: Fix first selected manifest

* gramma..

* Fix 61b3259

* length
2025-08-10 23:04:34 +00:00
HUMORCE
22f1672fcb fix(scoop-uninstall): Fix uninstaller does not gain Global state (#6430)
* global arg

* changelog
2025-08-10 22:48:06 +00:00
Bassel Rachid
4e528e1655 fix(decompress): Replace deprecated 7ZIPEXTRACT_USE_EXTERNAL config (#6327)
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2025-04-17 11:19:22 +08:00
Hsiao-nan Cheung
ac6f1baaf4 perf(shim): Update kiennq-shim to v3.1.2 (#6261) 2025-01-03 14:34:38 +08:00
Hsiao-nan Cheung
dc9d198014 docs(chglog): Update to 0.5.3 (#6258) 2024-12-30 13:55:32 +08:00
Chawye Hsu
84e00fdb77 fix(scoop-bucket): Add missing import for no_junction envs (#6181)
Signed-off-by: Chawye Hsu <su+git@chawyehsu.com>
2024-10-11 14:20:43 +08:00
Ryan
7a309a1b00 fix(shim): properly check wslpath/cygpath command first (#6114)
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2024-10-09 19:10:42 +08:00
HUMORCE
3577f91d82 fix(commands): Handling broken aliases (#6141) 2024-09-20 14:18:13 +08:00
kiennq
e0c682de7c fix(download): Fallback to default downloader when aria2 fails (#4292) 2024-08-26 16:10:30 +08:00
Hsiao-nan Cheung
79cf33d0b7 refactor(download): Move download-related functions to 'download.ps1' (#6095) 2024-08-11 17:23:59 +08:00
Olav Rønnestad Birkeland
7f99c499d7 fix (decompress): Expand-7zipArchive only delete temp dir / $extractDir if it is empty (#6092)
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2024-08-09 11:03:51 +08:00
Hsiao-nan Cheung
859d1db51b chore(release): Bump to version 0.5.2 (#6080)
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
Co-authored-by: Matt Cargile <74641867+mattcargile@users.noreply.github.com>
Co-authored-by: ccyykkcyk <91893497+ccyykkcyk@users.noreply.github.com>
2024-07-30 15:34:41 +08:00
Hsiao-nan Cheung
c7ec5c82b2 docs(chglog): Update CHANGELOG for v0.5.2 (#6081) 2024-07-25 20:29:02 +08:00
Hsiao-nan Cheung
46624b00c9 fix(core): Use 'Join-Path' to construct cache file path (#6079) 2024-07-25 13:54:26 +08:00
Hsiao-nan Cheung
49ee8ad6ec fix(scoop-hold): Use 'foreach' loop to allow 'continue' statement (#6078) 2024-07-25 13:00:36 +08:00
Hsiao-nan Cheung
5dc5dd22f8 fix(json): Fix JSON items counting function (#6073) 2024-07-23 19:55:40 +08:00
Hsiao-nan Cheung
5971f1dda8 builds(supporting): Update Json.Schema to 4.0.1 (#6072) 2024-07-23 19:13:00 +08:00
ccyykkcyk
0cb479abf0 fix(json): Don't serialize jsonpath return if only one result (#6066)
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2024-07-22 19:02:06 +08:00
Matt Cargile
429ba7af5b fix(scoop-alias): Fix 'Option --verbose not recognized.' (#6062) 2024-07-17 22:12:29 +08:00
Hsiao-nan Cheung
be56faf290 chore(release): Bump to version 0.5.1 (#6057) 2024-07-17 10:38:53 +08:00
Hsiao-nan Cheung
69edc6dc54 chore(chglog): Update CHANGELOG for v0.5.1 (#6058) 2024-07-15 13:17:19 +08:00
L. Yeung
0138dc4266 fix(scoop-alias): Pass options correctly (#6003)
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2024-07-15 13:06:22 +08:00
Bill ZHANG
1c271f5b90 fix(scoop-virustotal): Adjust json_path parameters to retrieve correct analysis stats (#6044) 2024-07-09 13:45:09 +08:00
qiuyk
a76884af19 fix(install): Fix parsing error when installing multiple apps w/ specific version (#6039)
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2024-07-09 13:22:06 +08:00
Hsiao-nan Cheung
7cc4faea1d fix(bucket): Implement error handling for failed bucket addition (#6051) 2024-07-09 01:16:23 +08:00
Hsiao-nan Cheung
83f25a4673 fix(install): Expand env_set items (#6050) 2024-07-09 00:24:42 +08:00
Hsiao-nan Cheung
42af27d16c fix(sqlite): Fix compatibility with Windows PowerShell (#6045) 2024-07-08 13:43:22 +08:00
Hsiao-nan Cheung
716b6db3ae chore(release): Bump to version 0.5.0 (#6035) 2024-07-05 11:14:42 +08:00
Hsiao-nan Cheung
ade7aa4a15 (chore): Update changelog (#6036) 2024-06-30 18:19:19 +08:00
Hsiao-nan Cheung
7e0a2a28c6 Update chglog 2024-06-30 18:09:59 +08:00
Hsiao-nan Cheung
2d02483fb8 Merge branch 'master' into develop 2024-06-30 18:07:36 +08:00
Hsiao-nan Cheung
93359a43a1 fix(shim): Restore original path for JAR cmd (#6030) 2024-06-26 18:34:38 +08:00
L. Yeung
d8b3cc8a6c fix(checkver): Correct error messages (#6024) 2024-06-25 10:40:56 +08:00
Hsiao-nan Cheung
9239c26bbd feat(decompress): Use innounp-unicode as default Inno Setup Unpacker (#6028) 2024-06-24 21:20:29 +08:00
qiuyk
3a39ba048f fix(core): Limit the number of commands to get (#6013) 2024-06-14 00:18:14 +08:00
Hsiao-nan Cheung
a9ca75c0cd fix(core): Use correct path in 'bash' (#6006) 2024-06-11 11:03:35 +08:00
Matej Kafka
d20819e147 fix(core): Search for Git executable instead of any cmdlet (#5998) 2024-06-07 18:45:52 +08:00
L. Yeung
dec4232754 fix(decompress): Match extract_dir/extract_to and archives (#5983) 2024-05-26 15:36:06 +08:00
Chawye Hsu
fa1b42bf28 fix(checkver): Correct variable 'regex' to 'regexp' (#5993) 2024-05-26 14:30:47 +08:00
insertokername
700a2f4f5e feat(install): Added the ability to specify @ version when installing from url/file location (#5988) 2024-05-23 23:05:38 +08:00
Hsiao-nan Cheung
c9048ad978 fix(sqlite): Fix generic class error in PS5 (#5981) 2024-05-20 20:57:29 +08:00
Hsiao-nan Cheung
5c896e901f refactor(decompress): Use 7zip to extract Zstd archive (#5973) 2024-05-17 09:56:14 +08:00
Hsiao-nan Cheung
8ea37387ae fix(install): Add back arg $Name in Invoke-Installer() (#5971) 2024-05-15 19:25:35 +08:00
Hsiao-nan Cheung
2544745695 refactor(install): Replace 'run_(un)installer()' with 'Invoke-Installer()' (#5968) 2024-05-15 19:03:54 +08:00
Hsiao-nan Cheung
5ce70c4139 fix(sqlite): Update cache after removing bucket or manifests (#5967) 2024-05-15 17:07:37 +08:00
L. Yeung
2d50a02a32 fix(scoop-download|install|update): Use consistent options (#5956) 2024-05-14 22:43:53 +08:00
Hsiao-nan Cheung
f6f46f6cf4 fix(sqlite): Dispose all command to release database file handle (#5966) 2024-05-14 16:38:05 +08:00
Hsiao-nan Cheung
d337bb1fa6 chore(release): Bump to version 0.4.2 (#5964)
Co-authored-by: Xuesong <amorphobia@users.noreply.github.com>
Co-authored-by: Inseo Lee <capella87@outlook.com>
2024-05-14 13:44:20 +08:00
Hsiao-nan Cheung
cddcd98e33 doc(chglog): Update CHANGLOG 2024-05-13 19:02:43 +08:00
Hsiao-nan Cheung
86d3fef187 fix(core): Add 'PSObject.Copy()' to 'substitute()' (#5962) 2024-05-13 18:53:30 +08:00
Hsiao-nan Cheung
e324fa4c5f fix(core): Fix "Invoke-ExternalCommand" quoting rules (#5945) 2024-05-13 18:53:15 +08:00
Inseo Lee
d9c5c90285 fix(system): Fix argument passing to Split-PathLikeEnvVar() in deprecated strip_path() (#5937)
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2024-05-13 18:53:15 +08:00
Xuesong
82640c456e fix(autoupdate): Copy PSCustomObject-type properties within autoupdate to prevent reference changes (#5934) 2024-05-13 18:53:15 +08:00
Hsiao-nan Cheung
4dd2cfdc5f fix(core): Add 'PSObject.Copy()' to 'substitute()' (#5962) 2024-05-13 13:46:59 +08:00
Hsiao-nan Cheung
a5bd2297c6 refactor(install): Separate archive extraction from downloader (#5951) 2024-05-12 18:47:16 +08:00
Hsiao-nan Cheung
b710ff6c0a fix(scoop-info): Fix download size estimating (#5958) 2024-05-11 18:22:31 +08:00
Hsiao-nan Cheung
1dd479f0be fix(database): Use 'Find-BucketDirectory()' to locate bucket dir (#5955) 2024-05-11 14:50:20 +08:00
Hsiao-nan Cheung
1752050614 fix(core): Fix "Invoke-ExternalCommand" quoting rules (#5945) 2024-05-11 14:49:49 +08:00
Hsiao-nan Cheung
edaae8d39f fix(database): Skip caching 'deprecated' dir (#5949) 2024-05-09 20:15:57 +08:00
Hsiao-nan Cheung
776135e0ab fix(database): Update cache when adding bucket (#5946) 2024-05-08 18:10:13 +08:00
Chawye Hsu
cc863353e2 fix(scoop-cache): Fix regression in 36026f18 (#5944)
Signed-off-by: Chawye Hsu <su+git@chawyehsu.com>
2024-05-08 11:24:41 +08:00
Inseo Lee
5b86c302e5 fix(system): Fix argument passing to Split-PathLikeEnvVar() in deprecated strip_path() (#5937)
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2024-05-06 10:04:40 +08:00
Xuesong
b8580aa931 fix(autoupdate): Copy PSCustomObject-type properties within autoupdate to prevent reference changes (#5934) 2024-05-01 23:02:13 +08:00
Chawye Hsu
23d55ced72 fix(scoop-search): Catch error of parsing invalid manifest (#5930) 2024-05-01 00:52:49 +08:00
Chawye Hsu
36026f1804 feat(core): New cache filename format (#5929)
Signed-off-by: Chawye Hsu <su+git@chawyehsu.com>
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2024-04-29 18:36:19 +08:00
Chawye Hsu
3b34497eb4 fix(json): Serialize jsonpath return (#5921) 2024-04-26 18:12:34 +08:00
Hsiao-nan Cheung
bb88dfb2d4 Sync 'master' branch 2024-04-25 22:09:16 +08:00
Chawye Hsu
d285bb08d4 chore(release): Bump to version 0.4.1 (#5924)
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2024-04-25 21:33:45 +08:00
Hsiao-nan Cheung
eac58400db fix(sqlite): Skip use_sqlite_cache config on ARM64 platform (#5918) 2024-04-25 21:32:41 +08:00
Hsiao-nan Cheung
2dd91d5ba3 feat(sqlite): Use SQLite for caching apps to speed up local search (#5851) 2024-04-19 14:02:50 +08:00
Hsiao-nan Cheung
105e4161fc chore(release): Bump to version 0.4.0 (#5424)
Co-authored-by: Richard Kuhnt <r15ch13+git@gmail.com>
Co-authored-by: HUMORCE <humorce@outlook.com>
Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
Co-authored-by: Ross Smith II <ross@smithii.com>
Co-authored-by: Ercolino <fabrizio.ciccarese@gmail.com>
Co-authored-by: Jeppe Frandsen <jeppe@algrens.dk>
Co-authored-by: Chawye Hsu <chawyehsu@hotmail.com>
Co-authored-by: 0x574859 <Hex574859@outlook.com>
Co-authored-by: Valinor <Valinor@users.noreply.github.com>
Co-authored-by: Stephen Albert-Moore <stephen@sherbet.space>
Co-authored-by: L. Yeung <lewis_yeung-ly@outlook.com>
Co-authored-by: Bill ZHANG <36790218+Lutra-Fs@users.noreply.github.com>
Co-authored-by: walpo <68171111+walpox@users.noreply.github.com>
Co-authored-by: Suhas <98212676+spider2048@users.noreply.github.com>
Co-authored-by: Gerardo Grignoli <gerardog@gmail.com>
Co-authored-by: Dustin <131928587+dooptydoo90x@users.noreply.github.com>
Co-authored-by: Hagai Gold <hagaigold@gmail.com>
Co-authored-by: David Watson <davidlwatsonjr@users.noreply.github.com>
Co-authored-by: András Svraka <svraka.andras@gmail.com>
Co-authored-by: Xuesong <amorphobia@users.noreply.github.com>
2024-04-19 00:07:58 +08:00
Hsiao-nan Cheung
78f6fecd60 docs(changelog): Rearrange and update CHANGELOG (#5904) 2024-04-18 20:05:13 +08:00
Hsiao-nan Cheung
5819b5a1ef feat(path): Isolate Scoop apps' PATH (#5840) 2024-04-18 19:14:53 +08:00
Hsiao-nan Cheung
fa06e921c2 refactor(shim): Remove CS shim codebase (#5903) 2024-04-18 19:09:08 +08:00
Hsiao-nan Cheung
98cf8ae4da fix(autoupdate): Fix bug that 'WebClient' doesn't auto-extract 'gzip' (#5901) 2024-04-17 22:29:20 +08:00
Hsiao-nan Cheung
7054c9d338 fix(decompress): Use wix.exe in WiX Toolset v4+ as primary extractor of Expand-DarkArchive() (#5871) 2024-04-12 16:09:11 +08:00
Hsiao-nan Cheung
6327146b97 fix(shim): Run JAR file from app's root directory (#5872) 2024-04-11 15:56:43 +08:00
Hsiao-nan Cheung
92b71c6057 refactor(core): Get rid of 'fullpath' (#3533) 2024-04-10 14:55:20 +08:00
HUMORCE
81e7dec78c fix(scoop-alias): Prevent overwrite existing file when adding alias (#5577)
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2024-04-08 23:26:38 +08:00
Xuesong
b008fe5b11 fix(scoop-virustotal): escape character not available in PowerShell 5.1 (#5870) 2024-04-08 23:24:18 +08:00
HUMORCE
5a06eacd9c refactor(helper): Remove 7zip's fallback '7zip-zstd' (#5548) 2024-04-08 23:23:57 +08:00
András Svraka
0b135052ce fix(scoop-shim): Import system.ps1 (#5864) 2024-04-04 21:39:51 +08:00
David Watson
9ef03c24fb perf(scoop-update): Check for running process before wasting time on download (#5799) 2024-04-01 18:31:01 +08:00
Hsiao-nan Cheung
dfbeace066 fix(system): Remove EnvVar only if existed (#5858) 2024-04-01 18:02:32 +08:00
Hsiao-nan Cheung
6772e61b3d refactor(core): Rewrite and separate path-related functions to system.ps1 (#5836) 2024-03-27 17:32:39 +08:00
Hsiao-nan Cheung
77b66cc890 fix(core): Fix arguments parsing method of Invoke-ExternalCommand() (#5839) 2024-03-25 19:19:44 +08:00
Hsiao-nan Cheung
9770c86598 fix(shim): Update kiennq-shim to v3.1.1 (#5847) 2024-03-25 18:07:31 +08:00
Hsiao-nan Cheung
5153d7375b perf(shim): Update kiennq-shim to v3.1 (#5841) 2024-03-22 19:06:27 +08:00
Hsiao-nan Cheung
5354ab5d16 builds(supporting): Update Json to 13.0.3, Json.Schema to 3.0.15 (#5835) 2024-03-21 13:30:51 +08:00
Suhas
90766f9315 fix(shim): Allow GUI applications to attach to the shell's console when launched using the GUI shim (#5721) 2024-03-20 22:16:10 +08:00
Hsiao-nan Cheung
3186fef105 fix(update/uninstall): Remove items from PATH correctly (#5833) 2024-03-20 17:17:48 +08:00
Hsiao-nan Cheung
9d07c33e87 fix(decompress): Remove unused parent dir w/ 'extract_dir' (#5682) 2024-03-13 18:41:58 +08:00
Hsiao-nan Cheung
6f9ed1d464 fix(ci): Update 'psmodulecache' version to 'main' (#5828) 2024-03-13 13:22:58 +08:00
Hsiao-nan Cheung
54e0514833 fix(install): Fix bugs in #5715 (#5824) 2024-03-07 10:55:27 +08:00
Hsiao-nan Cheung
7e3dc73b83 refactor(core): Cleanup some old codes, e.g., msi section and config migration (#5715) 2024-03-06 21:04:46 +08:00
HUMORCE
48f793532c fix(manifest): Correct source of manifest (#5575) 2024-02-23 17:41:44 +08:00
Rashil Gandhi
5328bef269 fix(config): Warn users about misconfigured token (#5777)
* Warn users about misconfigured token

* Update CHANGELOG.md
2024-01-05 23:46:28 +05:30
Rashil Gandhi
fb3169629f fix(scoop-checkup): Don't throw 7zip error when external 7zip is used (#5703)
* fix(scoop-checkup): Don't throw 7zip error when external 7zip is used

* Update CHANGELOG.md
2023-10-26 14:13:25 +05:30
Bill ZHANG
7e81e49152 fix(scoop-reset): Don't abort when multiple apps are passed and an app is running (#5687)
* fix(scoop-reset): change #2952 fix to be the same with scoop-install

* update changelog

* Update CHANGELOG.md

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

---------

Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
2023-10-26 12:58:10 +05:30
Hagai Gold
7b35e19d4c fix(perf): Do not call scoop externally from inside the code (#5695)
* fix: do not call `scoop` externally from inside the code

* update CHANGELOG.md

* update CHANGELOG.md
2023-10-19 13:19:31 +05:30
Sp1d3R
6cdcc75ad8 fix(shim): Fix a minor issue with Get-Subsystem (#5684)
* Fix PE read error and refractor

* refactor Change-Subsystem -> Set-Subsystem and additional `catch` block

* refactor Change-Subsystem -> Set-Subsystem and additional `catch` block

* add a return value to `Set-PESubsystem`

* fix trailing whitespace
2023-10-14 16:04:46 +05:30
L. Yeung
14b38b4092 fix(scoop-shim): Check literal path in Get-ShimPath (#5680)
Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
2023-10-10 16:50:53 +05:30
L. Yeung
2847e0a37c fix(scoop-shim): Avoid unexpected output of list subcommand (#5681)
* fix(shim): Avoid unexpected output of `list` subcommand

* Update libexec/scoop-shim.ps1

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

---------

Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2023-10-10 16:47:46 +05:30
Dustin
3a3f41c556 fix(shim): Fix shim adding bug related to Resolve-Path(#5493)
* fixed shim add bug related to #5492

* Updated CHANGLOG.md per contributor guidelines
2023-10-08 19:04:36 +05:30
Rashil Gandhi
15f9bbec97 fix(update): Change error message to a better instruction (#5677)
* fix(update): Change error message to a better instruction

* Update CHANGELOG.md
2023-10-06 14:51:45 +05:30
Gerardo Grignoli
ab34b7fb61 feat(core): Allow global install of PowerShell modules (#5611)
* Allow global install of PowerShell modules to $env:ProgramFiles\WindowsPowerShell\Modules

* tabs vs spaces

* Updated CHANGELOG.md

* Changed global install of PowerShell modules to $globaldir\Modules
2023-10-05 16:40:03 +05:30
HUMORCE
863af42d4e fix(core): Fix detection of Git (#5545)
* fix(core): Fix `Test-GitAvailable`

* fix(core): Fallback git(32bit) to `Get-HelperPath`

* check value of `$internalgit` also

* changlog

* path already tested when invoke `Get-AppFilePAth`
2023-10-05 12:21:29 +05:30
Richard Kuhnt
b3c05e71fa perf(scoop-search): Improve performance for local search (#5644)
* perf(search): improve local search performance

* Update libexec/scoop-search.ps1

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

* Update libexec/scoop-search.ps1

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

* Update libexec/scoop-search.ps1

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

* Update libexec/scoop-search.ps1

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

* Update libexec/scoop-search.ps1

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

* Update libexec/scoop-search.ps1

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

* Added [JsonDocument]::Parse for testing

* Fix array length check

* Used wrong function

* Add fallback function for PowerShell 5

* Check for System.Text.Json in Assemblies instead

* Show help output

* Revert "Show help output"

This reverts commit d3d6b01d08.

* Update CHANGELOG.md

---------

Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
2023-10-03 19:35:45 +05:30
HUMORCE
acc271d115 fix(scoop-(un)hold): Correct output the messages when manifest not found, (already|not) held (#5554)
* fix(scoop-(un)hold): Abort when manifest not found

* abort when already held or not held

* use continue

* CHANGELOG

* Update CHANGELOG.md

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

---------

Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
2023-10-03 19:27:01 +05:30
HUMORCE
6d79d62cc8 fix(core): Handle scoop aliases and broken (edited,copied) shim (#5551)
* fix(core): Avoid error messages when shim broked, Allow get path for scoop aliases

* update `scoop-which`

* Add CHANGELOG entry

* Update CHANGELOG.md

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

---------

Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
2023-10-03 18:59:10 +05:30
HUMORCE
00c92b04d6 fix(install): Avoid error when unlinking non-existent junction/hardlink (#5552)
* fix(install): Avoid error when unlinking non-existent junction / hardlink

* CHANGELOG

* Update CHANGELOG.md

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

---------

Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
2023-10-03 18:58:02 +05:30
HUMORCE
becc7a7b76 fix(core): Avoid error messages when deleting non-existent environment variable (#5547)
* fix(core): Check if the envrionment variable exists before delete

* CHANGELOG

* Use `try, catch`, value of env variable may is null/empty

* UPDATE CHANGELOG

* Brackets

---------

Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
2023-10-03 16:29:44 +05:30
HUMORCE
6898773a8d fix(core): Use relative path as fallback of $scoopdir (#5544)
* fix(core): Use relative path as fallback of `$scoopdir`(Scoop root directory)

* typo

* changelog

* re

---------

Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
2023-10-03 16:23:00 +05:30
Sp1d3R
353137f0a9 fix(shim): Remove console window for GUI apps (#5559)
* Change shim subsystem to prevent console window for GUI apps.

* Removed redundant log

* Update core.ps1

* Fixes file access rights and the log message

* update changelog

* Update CHANGELOG.md (PR Number)

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

---------

Co-authored-by: Quasar <suhas.nandibhatla@gmail.com>
Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
2023-10-03 00:09:48 +05:30
Rashil Gandhi
43579714cc fix(env): Publish environment change when deleting variable (#5631)
* fix(env): Publish environment change when deleting variable

* Update CHANGELOG.md
2023-10-02 20:59:50 +05:30
walpo
aa09601503 docs(readme): Improve documentation language (#5638)
* readme.md: refactor HTML blocks

* readme.md: enhance style and fix typos and more

* readme.md: improve composition

* Update README.md

It seems GitHub does not work well with the 'style' attribute, so the deprecated attribute 'align' cannot be replaced.

* readme.md: update writing

* Update README.md

* Update README.md

* Update CHANGELOG.md

---------

Co-authored-by: walpo <3212100-walpo@users.noreply.gitlab.com>
Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
2023-10-02 14:36:06 +05:30
HUMORCE
6a35a22b0b fix(scoop-checkup): Change the message level of helpers from ERROR to WARN (#5614)
* fix(scoop-checkup): Downgrade the message level of helpers from ERROR to WARN

* update CHANGELOG

---------

Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
2023-09-11 13:50:07 +05:30
Bill ZHANG
0b4919ca32 fix(scoop-virustotal): Continue execution when no app but -a flag is provided (#5593)
* fix(scoop-virustotal): continue exec when no app but -a provided

* update CHANGELOG.md

* CHANGELOG: fix typo

---------

Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
2023-09-11 00:28:53 +05:30
Bill ZHANG
efdd6dd7ca docs(CHANGELOG): Add missing brackets for the links (#5596)
add missing brackets for the links
2023-08-11 18:46:58 +05:30
HUMORCE
3dfb4bfd97 fix(buckets): Avoid error messages for unexpected dir (#5549) 2023-06-30 00:18:19 +08:00
HUMORCE
1d140585a4 fix(scoop-checkup): Skip defender check in Windows Sandbox (#5519) 2023-05-30 13:46:05 +08:00
0x574859
52059ca1ac fix(env): Apply env immediately by SendMessageTimeout (#5452) 2023-04-07 13:05:46 +08:00
HUMORCE
cddc52e03b docs(scoop-info): Fix help message (#5445) 2023-03-27 14:37:10 +08:00
Richard Kuhnt
682a1e2c07 fix(git): set HTTP(S)_PROXY only in process scope to prevent race condition (#5436) 2023-03-20 23:52:41 +08:00
Hsiao-nan Cheung
2accaae5d1 fix(core): Rewrite config file when needed (#5439) 2023-03-20 22:44:55 +08:00
Rashil Gandhi
a20bb4f1a6 fix(shim): Use bash executable directly (#5433)
* fix(shim): Use bash executable directly

* Update CHANGELOG.md
2023-03-16 20:09:18 +05:30
Ross Smith II
ad0f6178d0 feat(bucket): Switch nirsoft bucket to ScoopInstaller/Nirsoft (#5328) 2023-03-11 21:42:14 +08:00
L. Yeung
41620bb169 feat(core): Add -Silent switch for Invoke-ExternalCommand (#5346) 2023-03-11 21:39:45 +08:00
Hsiao-nan Cheung
559c6f9e64 feat(bucket): Make official buckets higher priority (#5398) 2023-03-10 17:24:41 +08:00
Richard Kuhnt
7826d6fe2d perf(decompress): disable progress bar to improve Expand-Archive performance (#5410)
See: https://github.com/ScoopInstaller/Install/pull/42

---------

Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2023-02-27 20:11:40 +01:00
Stephen Albert-Moore
8acfeeefcf fix(scoop-info) --verbose file size collection (#5352) 2023-02-26 20:19:54 +01:00
Valinor
c00dd42cae fix(getopt): Stop split arguments in getopt() and ensure array by explicit arguments type (#5326)
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2023-02-26 14:32:16 +08:00
Hsiao-nan Cheung
3f11454a3c fix(core): Fix scripts' calling parameters (#5365) 2023-02-26 14:31:38 +08:00
0x574859
0a39de86e2 fix(env): Avoid automatic expansion of %% in env (#5395)
Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
2023-02-25 20:38:09 +08:00
Chawye Hsu
c44e214743 feat(config): Support portable config file (#5369) 2023-02-25 20:22:56 +08:00
Jeppe Frandsen
7c6aeb240e fix(install): Fix downloading release assets from private GitHub repositories (#5361)
Fix download from private GitHub repos
2023-02-25 14:00:37 +05:30
Hsiao-nan Cheung
32ca856f63 fix(core): Fix is_in_dir under Unix (#5391) 2023-02-19 23:56:53 +08:00
Ercolino
1d0bd434ab builds(checkver): Read the private_host config variable (#5381) 2023-02-19 17:19:01 +08:00
Ross Smith II
54e3613fca ci(dependabot): Add dependabot.yml for GitHub Actions (#5377) 2023-02-18 22:50:08 +08:00
Rashil Gandhi
e2558ace75 fix(chore): Handle spaces in git command arguments (#5375)
* fix(chore): Handle spaces in git command arguments

* changelog

* Use splatting
2023-02-07 04:55:54 +05:30
HUMORCE
68760de1e8 fix(shortcuts): Output correctly formatted path (#5333) 2023-01-09 11:01:40 +08:00
94 changed files with 3221 additions and 2273 deletions

8
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
---
# ~/.github/dependabot.yml
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/" # == /.github/workflows/
schedule:
interval: "daily"

View File

@@ -14,7 +14,7 @@ jobs:
with:
fetch-depth: 2
- name: Init Test Suite
uses: potatoqualitee/psmodulecache@v5.1
uses: potatoqualitee/psmodulecache@main
with:
modules-to-cache: BuildHelpers
shell: powershell
@@ -30,7 +30,7 @@ jobs:
with:
fetch-depth: 2
- name: Init Test Suite
uses: potatoqualitee/psmodulecache@v5.1
uses: potatoqualitee/psmodulecache@main
with:
modules-to-cache: BuildHelpers
shell: pwsh

1
.gitignore vendored
View File

@@ -6,3 +6,4 @@ test/installer/tmp/*
test/tmp/*
*~
TestResults.xml
supporting/sqlite/*

View File

@@ -1,23 +1,201 @@
## [Unreleased](https://github.com/ScoopInstaller/Scoop/compare/master...develop)
## [Unreleased](https://github.com/ScoopInstaller/Scoop/compare/v0.5.3...develop)
### Features
- **install:** Add separator at the end of notes, highlight suggestions ([#6418](https://github.com/ScoopInstaller/Scoop/issues/6418))
### Bug Fixes
- **scoop-download:** Fix function `nightly_version` not defined error ([#6386](https://github.com/ScoopInstaller/Scoop/issues/6386))
- **autoupdate:** Use origin URL to handle URLs with fragment in GitHub mode ([#6455](https://github.com/ScoopInstaller/Scoop/issues/6455))
- **buckets|scoop-info:** Switch git log date format to ISO 8601 to avoid locale issues ([#6446](https://github.com/ScoopInstaller/Scoop/issues/6446))
- **scoop-version:** Fix logic error caused by missing brackets ([#6463](https://github.com/ScoopInstaller/Scoop/issues/6463))
- **core|manifest:** Avoid error messages when searching non-existent 'deprecated' directory ([#6471](https://github.com/ScoopInstaller/Scoop/issues/6471))
## [v0.5.3](https://github.com/ScoopInstaller/Scoop/compare/v0.5.2...v0.5.3) - 2025-08-11
### Features
**autoupdate:** GitHub predefined hashes support ([#6416](https://github.com/ScoopInstaller/Scoop/issues/6416), [#6435](https://github.com/ScoopInstaller/Scoop/issues/6435))
### Bug Fixes
- **scoop-download|install|update:** Fallback to default downloader when aria2 fails ([#4292](https://github.com/ScoopInstaller/Scoop/issues/4292))
- **decompress**: `Expand-7zipArchive` only delete temp dir / `$extractDir` if it is empty ([#6092](https://github.com/ScoopInstaller/Scoop/issues/6092))
- **decompress**: Replace deprecated 7ZIPEXTRACT_USE_EXTERNAL config with USE_EXTERNAL_7ZIP ([#6327](https://github.com/ScoopInstaller/Scoop/issues/6327))
- **commands**: Handling broken aliases ([#6141](https://github.com/ScoopInstaller/Scoop/issues/6141))
- **shim:** Do not suppress `stderr`, properly check `wslpath`/`cygpath` command first ([#6114](https://github.com/ScoopInstaller/Scoop/issues/6114))
- **scoop-bucket:** Add missing import for `no_junction` envs ([#6181](https://github.com/ScoopInstaller/Scoop/issues/6181))
- **scoop-uninstall:** Fix uninstaller does not gain Global state ([#6430](https://github.com/ScoopInstaller/Scoop/issues/6430))
- **scoop-depends-tests:** Mocking `USE_EXTERNAL_7ZIP` as $false to avoding error when it is $true ([#6431](https://github.com/ScoopInstaller/Scoop/issues/6431))
### Code Refactoring
- **download:** Move download-related functions to 'download.ps1' ([#6095](https://github.com/ScoopInstaller/Scoop/issues/6095))
- **Get-Manifest:** Select actual source for manifest ([#6142](https://github.com/ScoopInstaller/Scoop/issues/6142))
### Performance Improvements
- **shim:** Update kiennq-shim to v3.1.2 ([#6261](https://github.com/ScoopInstaller/Scoop/issues/6261))
## [v0.5.2](https://github.com/ScoopInstaller/Scoop/compare/v0.5.1...v0.5.2) - 2024-07-26
### Bug Fixes
- **scoop-alias:** Fix 'Option --verbose not recognized.' ([#6062](https://github.com/ScoopInstaller/Scoop/issues/6062))
- **scoop-hold:** Use 'foreach' loop to allow 'continue' statement ([#6078](https://github.com/ScoopInstaller/Scoop/issues/6078))
- **core:** Use 'Join-Path' to construct cache file path ([#6079](https://github.com/ScoopInstaller/Scoop/issues/6079))
- **json:** Don't serialize jsonpath return if only one result ([#6066](https://github.com/ScoopInstaller/Scoop/issues/6066), [#6073](https://github.com/ScoopInstaller/Scoop/issues/6073))
### Builds
- **supporting:** Update Json.Schema to 4.0.1 ([#6072](https://github.com/ScoopInstaller/Scoop/issues/6072))
## [v0.5.1](https://github.com/ScoopInstaller/Scoop/compare/v0.5.0...v0.5.1) - 2024-07-16
### Bug Fixes
- **scoop-alias:** Pass options correctly ([#6003](https://github.com/ScoopInstaller/Scoop/issues/6003))
- **scoop-virustotal:** Adjust `json_path` parameters to retrieve correct analysis stats ([#6044](https://github.com/ScoopInstaller/Scoop/issues/6044))
- **bucket:** Implement error handling for failed bucket addition ([#6051](https://github.com/ScoopInstaller/Scoop/issues/6051))
- **database:** Fix compatibility with Windows PowerShell ([#6045](https://github.com/ScoopInstaller/Scoop/issues/6045))
- **install:** Expand `env_set` items before setting Environment Variables ([#6050](https://github.com/ScoopInstaller/Scoop/issues/6050))
- **install:** Fix parsing error when installing multiple apps w/ specific version ([#6039](https://github.com/ScoopInstaller/Scoop/issues/6039))
## [v0.5.0](https://github.com/ScoopInstaller/Scoop/compare/v0.4.2...v0.5.0) - 2024-07-01
### Features
- **scoop-search:** Use SQLite for caching apps to speed up local search ([#5851](https://github.com/ScoopInstaller/Scoop/issues/5851), [#5918](https://github.com/ScoopInstaller/Scoop/issues/5918), [#5946](https://github.com/ScoopInstaller/Scoop/issues/5946), [#5949](https://github.com/ScoopInstaller/Scoop/issues/5949), [#5955](https://github.com/ScoopInstaller/Scoop/issues/5955), [#5966](https://github.com/ScoopInstaller/Scoop/issues/5966), [#5967](https://github.com/ScoopInstaller/Scoop/issues/5967), [#5981](https://github.com/ScoopInstaller/Scoop/issues/5981))
- **core:** New cache filename format ([#5929](https://github.com/ScoopInstaller/Scoop/issues/5929), [#5944](https://github.com/ScoopInstaller/Scoop/issues/5944))
- **decompress:** Use innounp-unicode as default Inno Setup Unpacker ([#6028](https://github.com/ScoopInstaller/Scoop/issues/6028))
- **install:** Added the ability to install specific version of app from URL/file link ([#5988](https://github.com/ScoopInstaller/Scoop/issues/5988))
### Bug Fixes
- **scoop-download|install|update:** Use consistent options ([#5956](https://github.com/ScoopInstaller/Scoop/issues/5956))
- **scoop-info:** Fix download size estimating ([#5958](https://github.com/ScoopInstaller/Scoop/issues/5958))
- **scoop-search:** Catch error of parsing invalid manifest ([#5930](https://github.com/ScoopInstaller/Scoop/issues/5930))
- **checkver:** Correct variable 'regex' to 'regexp' ([#5993](https://github.com/ScoopInstaller/Scoop/issues/5993))
- **checkver:** Correct error messages ([#6024](https://github.com/ScoopInstaller/Scoop/issues/6024))
- **core:** Search for Git executable instead of any cmdlet ([#5998](https://github.com/ScoopInstaller/Scoop/issues/5998))
- **core:** Use correct path in 'bash' ([#6006](https://github.com/ScoopInstaller/Scoop/issues/6006))
- **core:** Limit the number of commands to get when search for git executable ([#6013](https://github.com/ScoopInstaller/Scoop/issues/6013))
- **decompress:** Match `extract_dir`/`extract_to` and archives ([#5983](https://github.com/ScoopInstaller/Scoop/issues/5983))
- **json:** Serialize jsonpath return ([#5921](https://github.com/ScoopInstaller/Scoop/issues/5921))
- **shim:** Restore original path for JAR cmd ([#6030](https://github.com/ScoopInstaller/Scoop/issues/6030))
### Code Refactoring
- **decompress:** Use 7zip to extract Zstd archive ([#5973](https://github.com/ScoopInstaller/Scoop/issues/5973))
- **install:** Separate archive extraction from downloader ([#5951](https://github.com/ScoopInstaller/Scoop/issues/5951))
- **install:** Replace 'run_(un)installer()' with 'Invoke-Installer()' ([#5968](https://github.com/ScoopInstaller/Scoop/issues/5968), [#5971](https://github.com/ScoopInstaller/Scoop/issues/5971))
## [v0.4.2](https://github.com/ScoopInstaller/Scoop/compare/v0.4.1...v0.4.2) - 2024-05-14
### Bug Fixes
- **autoupdate:** Copy `PSCustomObject`-type properties within `substitute()` to prevent reference changes ([#5934](https://github.com/ScoopInstaller/Scoop/issues/5934), [#5962](https://github.com/ScoopInstaller/Scoop/issues/5962))
- **core:** Fix `Invoke-ExternalCommand` quoting rules ([#5945](https://github.com/ScoopInstaller/Scoop/issues/5945))
- **system:** Fix argument passing to `Split-PathLikeEnvVar()` in deprecated `strip_path()` ([#5937](https://github.com/ScoopInstaller/Scoop/issues/5937))
## [v0.4.1](https://github.com/ScoopInstaller/Scoop/compare/v0.4.0...v0.4.1) - 2024-04-25
### Bug Fixes
- **core:** Fix `Invoke-ExternalCommand` regression ([#5923](https://github.com/ScoopInstaller/Scoop/issues/5923))
## [v0.4.0](https://github.com/ScoopInstaller/Scoop/compare/v0.3.1...v0.4.0) - 2024-04-18
### Features
- **scoop-update:** Add support for parallel syncing buckets in PowerShell 7 and improve output ([#5122](https://github.com/ScoopInstaller/Scoop/issues/5122))
- **bucket:** Switch nirsoft bucket to ScoopInstaller/Nirsoft ([#5328](https://github.com/ScoopInstaller/Scoop/issues/5328))
- **bucket:** Make official buckets higher priority ([#5398](https://github.com/ScoopInstaller/Scoop/issues/5398))
- **config:** Support portable config file ([#5369](https://github.com/ScoopInstaller/Scoop/issues/5369))
- **core:** Add `-Quiet` switch for `Invoke-ExternalCommand` ([#5346](https://github.com/ScoopInstaller/Scoop/issues/5346))
- **core:** Allow global install of PowerShell modules ([#5611](https://github.com/ScoopInstaller/Scoop/issues/5611))
- **path:** Isolate Scoop apps' PATH ([#5840](https://github.com/ScoopInstaller/Scoop/issues/5840))
### Bug Fixes
- **decompress:** Exclude '*.nsis' that may cause error ([#5294](https://github.com/ScoopInstaller/Scoop/issues/5294))
- **scoop-alias:** Prevent overwrite existing file when adding alias ([#5577](https://github.com/ScoopInstaller/Scoop/issues/5577))
- **scoop-checkup:** Skip defender check in Windows Sandbox ([#5519](https://github.com/ScoopInstaller/Scoop/issues/5519))
- **scoop-checkup:** Change the message level of helpers from ERROR to WARN ([#5614](https://github.com/ScoopInstaller/Scoop/issues/5614))
- **scoop-checkup:** Don't throw 7zip error when external 7zip is used ([#5703](https://github.com/ScoopInstaller/Scoop/issues/5703))
- **scoop-(un)hold:** Correct output the messages when manifest not found, (already|not) held ([#5519](https://github.com/ScoopInstaller/Scoop/issues/5519))
- **scoop-info:** Fix errors in file size collection when `--verbose` ([#5352](https://github.com/ScoopInstaller/Scoop/issues/5352))
- **scoop-reset:** Don't abort when multiple apps are passed and an app is running ([#5687](https://github.com/ScoopInstaller/Scoop/issues/5687))
- **scoop-update:** Change error message to a better instruction ([#5677](https://github.com/ScoopInstaller/Scoop/issues/5677))
- **scoop-virustotal:** Fix `scoop-virustotal` when `--all` has been passed without app ([#5593](https://github.com/ScoopInstaller/Scoop/issues/5593))
- **scoop-virustotal:** Fix the issue that escape character not available in PowerShell 5.1 ([#5870](https://github.com/ScoopInstaller/Scoop/issues/5870))
- **autoupdate:** Fix file hash extraction ([#5295](https://github.com/ScoopInstaller/Scoop/issues/5295))
- **autoupdate:** Fix bug that 'WebClient' doesn't auto-extract 'gzip' ([#5901](https://github.com/ScoopInstaller/Scoop/issues/5901))
- **buckets:** Avoid error messages for unexpected dir ([#5549](https://github.com/ScoopInstaller/Scoop/issues/5549))
- **config:** Warn users about misconfigured GitHub token ([#5777](https://github.com/ScoopInstaller/Scoop/issues/5777))
- **core:** Fix scripts' calling parameters ([#5365](https://github.com/ScoopInstaller/Scoop/issues/5365))
- **core:** Fix `is_in_dir` under Unix ([#5391](https://github.com/ScoopInstaller/Scoop/issues/5391))
- **core:** Rewrite config file when needed ([#5439](https://github.com/ScoopInstaller/Scoop/issues/5439))
- **core:** Prevents leaking HTTP(S)_PROXY env vars to current sessions after Invoke-Git in parallel execution ([#5436](https://github.com/ScoopInstaller/Scoop/issues/5436))
- **core:** Handle scoop aliases and broken(edited,copied) shim ([#5551](https://github.com/ScoopInstaller/Scoop/issues/5551))
- **core:** Avoid error messages when deleting non-existent environment variable ([#5547](https://github.com/ScoopInstaller/Scoop/issues/5547))
- **core:** Use relative path as fallback of `$scoopdir` ([#5544](https://github.com/ScoopInstaller/Scoop/issues/5544))
- **core:** Fix detection of Git ([#5545](https://github.com/ScoopInstaller/Scoop/issues/5545))
- **core:** Do not call `scoop` externally from inside the code ([#5695](https://github.com/ScoopInstaller/Scoop/issues/5695))
- **core:** Fix arguments parsing method of `Invoke-ExternalCommand()` ([#5839](https://github.com/ScoopInstaller/Scoop/issues/5839))
- **decompress:** Exclude '*.nsis' that may cause error ([#5294](https://github.com/ScoopInstaller/Scoop/issues/5294))
- **decompress:** Remove unused parent dir w/ 'extract_dir' ([#5682](https://github.com/ScoopInstaller/Scoop/issues/5682))
- **decompress:** Use `wix.exe` in WiX Toolset v4+ as primary extractor of `Expand-DarkArchive()` ([#5871](https://github.com/ScoopInstaller/Scoop/issues/5871))
- **env:** Avoid automatic expansion of `%%` in env ([#5395](https://github.com/ScoopInstaller/Scoop/issues/5395), [#5452](https://github.com/ScoopInstaller/Scoop/issues/5452), [#5631](https://github.com/ScoopInstaller/Scoop/issues/5631))
- **getopt:** Stop split arguments in `getopt()` and ensure array by explicit arguments type ([#5326](https://github.com/ScoopInstaller/Scoop/issues/5326))
- **install:** Fix download from private GitHub repositories ([#5361](https://github.com/ScoopInstaller/Scoop/issues/5361))
- **install:** Avoid error when unlinking non-existent junction/hardlink ([#5552](https://github.com/ScoopInstaller/Scoop/issues/5552))
- **manifest:** Correct source of manifest ([#5575](https://github.com/ScoopInstaller/Scoop/issues/5575))
- **shim:** Remove console window for GUI applications ([#5559](https://github.com/ScoopInstaller/Scoop/issues/5559))
- **shim:** Use bash executable directly ([#5433](https://github.com/ScoopInstaller/Scoop/issues/5433))
- **shim:** Check literal path in `Get-ShimPath` ([#5680](https://github.com/ScoopInstaller/Scoop/issues/5680))
- **shim:** Avoid unexpected output of `list` subcommand ([#5681](https://github.com/ScoopInstaller/Scoop/issues/5681))
- **shim:** Allow GUI applications to attach to the shell's console when launched using the GUI shim ([#5721](https://github.com/ScoopInstaller/Scoop/issues/5721))
- **shim:** Run JAR file from app's root directory ([#5872](https://github.com/ScoopInstaller/Scoop/issues/5872))
- **shortcuts:** Output correctly formatted path ([#5333](https://github.com/ScoopInstaller/Scoop/issues/5333))
- **update/uninstall:** Remove items from PATH correctly ([#5833](https://github.com/ScoopInstaller/Scoop/issues/5833))
### Performance Improvements
- **scoop-search:** Improve performance for local search ([#5644](https://github.com/ScoopInstaller/Scoop/issues/5644))
- **scoop-update:** Check for running process before wasting time on download ([#5799](https://github.com/ScoopInstaller/Scoop/issues/5799))
- **decompress:** Disable progress bar to improve `Expand-Archive` performance ([#5410](https://github.com/ScoopInstaller/Scoop/issues/5410))
- **shim:** Update kiennq-shim to v3.1.1 ([#5841](https://github.com/ScoopInstaller/Scoop/issues/5841), [#5847](https://github.com/ScoopInstaller/Scoop/issues/5847))
### Code Refactoring
- **git:** Use Invoke-Git() with direct path to git.exe to prevent spawning shim subprocesses ([#5122](https://github.com/ScoopInstaller/Scoop/issues/5122))
- **scoop-download:** Output more detailed manifest information ([#5277](https://github.com/ScoopInstaller/Scoop/issues/5277))
- **core:** Cleanup some old codes, e.g., msi section and config migration ([#5715](https://github.com/ScoopInstaller/Scoop/issues/5715), [#5824](https://github.com/ScoopInstaller/Scoop/issues/5824))
- **core:** Rewrite and separate path-related functions to `system.ps1` ([#5836](https://github.com/ScoopInstaller/Scoop/issues/5836), [#5858](https://github.com/ScoopInstaller/Scoop/issues/5858), [#5864](https://github.com/ScoopInstaller/Scoop/issues/5864))
- **core:** Get rid of 'fullpath' ([#3533](https://github.com/ScoopInstaller/Scoop/issues/3533))
- **git:** Use Invoke-Git() with direct path to git.exe to prevent spawning shim subprocesses ([#5122](https://github.com/ScoopInstaller/Scoop/issues/5122), [#5375](https://github.com/ScoopInstaller/Scoop/issues/5375))
- **helper:** Remove 7zip's fallback '7zip-zstd' ([#5548](https://github.com/ScoopInstaller/Scoop/issues/5548))
- **shim:** Remove CS shim codebase ([#5903](https://github.com/ScoopInstaller/Scoop/issues/5903))
### Builds
- **checkver:** Read the private_host config variable ([#5381](https://github.com/ScoopInstaller/Scoop/issues/5381))
- **supporting:** Update Json to 13.0.3, Json.Schema to 3.0.15 ([#5835](https://github.com/ScoopInstaller/Scoop/issues/5835))
### Continuous Integration
- **dependabot:** Add dependabot.yml for GitHub Actions ([#5377](https://github.com/ScoopInstaller/Scoop/issues/5377))
- **module:** Update 'psmodulecache' version to 'main' ([#5828](https://github.com/ScoopInstaller/Scoop/issues/5828))
### Tests
- **bucket:** Skip manifest validation if no manifest changes ([#5270](https://github.com/ScoopInstaller/Scoop/issues/5270))
### Documentation
- **scoop-info:** Fix help message([#5445](https://github.com/ScoopInstaller/Scoop/issues/5445))
- **readme:** Improve documentation language ([#5638](https://github.com/ScoopInstaller/Scoop/issues/5638))
## [v0.3.1](https://github.com/ScoopInstaller/Scoop/compare/v0.3.0...v0.3.1) - 2022-11-15
### Features
@@ -34,6 +212,7 @@
- **shim:** Exit if shim creating failed 'cause no git ([#5225](https://github.com/ScoopInstaller/Scoop/issues/5225))
- **scoop-import:** Add correct architecture argument ([#5210](https://github.com/ScoopInstaller/Scoop/issues/5210))
- **scoop-config:** Output `[DateTime]` as `[String]` ([#5232](https://github.com/ScoopInstaller/Scoop/issues/5232))
- **shim:** fixed shim add bug related to Resolve-Path ([#5492](https://github.com/ScoopInstaller/Scoop/issues/5492))
### Code Refactoring

103
README.md
View File

@@ -1,17 +1,17 @@
<p align="center">
<h1 align="center">Scoop</h1>
<!--<img src="scoop.png" alt="Long live Scoop!"/>-->
<h1 align="center">Scoop</h1>
</p>
<p align="center">
<b><a href="https://github.com/ScoopInstaller/Scoop#what-does-scoop-do">Features</a></b>
|
<b><a href="https://github.com/ScoopInstaller/Scoop#installation">Installation</a></b>
|
<b><a href="https://github.com/ScoopInstaller/Scoop/wiki">Documentation</a></b>
<a href="https://github.com/ScoopInstaller/Scoop#what-does-scoop-do">Features</a>
|
<a href="https://github.com/ScoopInstaller/Scoop#installation">Installation</a>
|
<a href="https://github.com/ScoopInstaller/Scoop/wiki">Documentation</a>
</p>
- - -
<p align="center" >
---
<p align="center">
<a href="https://github.com/ScoopInstaller/Scoop">
<img src="https://img.shields.io/github/languages/code-size/ScoopInstaller/Scoop.svg" alt="Code Size" />
</a>
@@ -36,43 +36,48 @@ Scoop is a command-line installer for Windows.
## What does Scoop do?
Scoop installs programs from the command line with a minimal amount of friction. It:
Scoop installs apps from the command line with a minimal amount of friction. It:
- Eliminates permission popup windows
- Hides GUI wizard-style installers
- Prevents PATH pollution from installing lots of programs
- Avoids unexpected side-effects from installing and uninstalling programs
- Finds and installs dependencies automatically
- Performs all the extra setup steps itself to get a working program
- Eliminates [User Account Control](https://learn.microsoft.com/windows/security/application-security/application-control/user-account-control/) (UAC) prompt notifications.
- Hides the graphical user interface (GUI) of wizard-style installers.
- Prevents polluting the `PATH` environment variable. Normally, this variable gets cluttered as different apps are installed on the device.
- Avoids unexpected side effects from installing and uninstalling apps.
- Resolves and installs dependencies automatically.
- Performs all the necessary steps to get an app to a working state.
Scoop is very scriptable, so you can run repeatable setups to get your environment just the way you like, e.g.:
Scoop is quite script-friendly. Your environment can become the way you like by using repeatable setups. For example:
```powershell
```console
scoop install sudo
sudo scoop install 7zip git openssh --global
scoop install aria2 curl grep sed less touch
scoop install python ruby go perl
```
If you've built software that you'd like others to use, Scoop is an alternative to building an installer (e.g. MSI or InnoSetup) — you just need to zip your program and provide a JSON manifest that describes how to install it.
If you have built software that you would like others to use, Scoop is an alternative to building an installer (like MSI or InnoSetup). You just need to compress your app to a `.zip` file and provide a JSON manifest that describes how to install it.
## Installation
Run the following command from a **non-admin** PowerShell to install scoop to its default location `C:\Users\<YOUR USERNAME>\scoop`.
Run the following commands from a regular (non-admin) PowerShell terminal to install Scoop:
```powershell
iwr -useb get.scoop.sh | iex
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Invoke-RestMethod -Uri https://get.scoop.sh | Invoke-Expression
```
Advanced installation instruction and full documentation of the installer are available in [ScoopInstaller/Install](https://github.com/ScoopInstaller/Install). Please create new issues there if you have questions about the installation.
**Note**: The first command makes your device allow running the installation and management scripts. This is necessary because Windows 10 client devices restrict execution of any PowerShell scripts by default.
## [Documentation](https://github.com/ScoopInstaller/Scoop/wiki)
It will install Scoop to its default location:
`C:\Users\<YOUR USERNAME>\scoop`
You can find the complete documentation about the installer, including advanced installation configurations, in [ScoopInstaller/Install](https://github.com/ScoopInstaller/Install). Please create new issues there if you have questions about the installation.
## Multi-connection downloads with `aria2`
Scoop can utilize [`aria2`](https://github.com/aria2/aria2) to use multi-connection downloads. Simply install `aria2` through Scoop and it will be used for all downloads afterward.
```powershell
```console
scoop install aria2
```
@@ -90,54 +95,54 @@ You can tweak the following `aria2` settings with the `scoop config` command:
## Inspiration
- [Homebrew](http://mxcl.github.io/homebrew/)
- [sub](https://github.com/37signals/sub#readme)
- [Homebrew](https://brew.sh/)
- [Sub](https://signalvnoise.com/posts/3264-automating-with-convention-introducing-sub)
## What sort of apps can Scoop install?
The apps that install best with Scoop are commonly called "portable" apps: i.e. compressed program files that run stand-alone when extracted and don't have side-effects like changing the registry or putting files outside the program directory.
The apps that are most likely to get installed fine with Scoop are those referred to as "portable" apps. These apps are compressed files which can run standalone after being extracted. This type of apps does not produce side effects like changing the Windows Registry or placing files outside the app directory.
Since installers are common, Scoop supports them too (and their uninstallers).
Scoop is also great at handling single-file programs and Powershell scripts. These don't even need to be compressed. See the [runat](https://github.com/ScoopInstaller/Main/blob/master/bucket/runat.json) package for an example: it's really just a GitHub gist.
Scoop also supports installer files and their uninstallation methods. Likewise, it can handle single-file apps and PowerShell scripts. These do not even need to be compressed. See the [runat](https://github.com/ScoopInstaller/Main/blob/master/bucket/runat.json) package for an example: it is simply a GitHub gist.
### Contribute to this project
If you'd like to improve Scoop by adding features or fixing bugs, please read our [Contributing Guide](https://github.com/ScoopInstaller/.github/blob/main/.github/CONTRIBUTING.md).
If you would like to improve Scoop by adding features or fixing bugs, please read our [Contributing Guide](https://github.com/ScoopInstaller/.github/blob/main/.github/CONTRIBUTING.md).
### Support this project
If you find Scoop useful and would like to support ongoing development and maintenance, here's how:
If you find Scoop useful and would like to support the ongoing development and maintenance of this project, you can donate here:
- [PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=DM2SUH9EUXSKJ) (one-time donation)
- [PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=DM2SUH9EUXSKJ) (one-time donations)
## Known application buckets
The following buckets are known to scoop:
The following buckets are known to Scoop:
- [main](https://github.com/ScoopInstaller/Main) - Default bucket for the most common (mostly CLI) apps
- [extras](https://github.com/ScoopInstaller/Extras) - Apps that don't fit the main bucket's [criteria](https://github.com/ScoopInstaller/Scoop/wiki/Criteria-for-including-apps-in-the-main-bucket)
- [games](https://github.com/Calinou/scoop-games) - Open source/freeware games and game-related tools
- [nerd-fonts](https://github.com/matthewjberger/scoop-nerd-fonts) - Nerd Fonts
- [nirsoft](https://github.com/kodybrown/scoop-nirsoft) - Almost all of the [250+](https://rasa.github.io/scoop-directory/by-apps#kodybrown_scoop-nirsoft) apps from [Nirsoft](https://nirsoft.net)
- [sysinternals](https://github.com/niheaven/scoop-sysinternals) - Sysinternals Suite and all individual application from [Microsoft](https://learn.microsoft.com/sysinternals/)
- [java](https://github.com/ScoopInstaller/Java) - A collection of Java development kits (JDKs), Java runtime engines (JREs), Java's virtual machine debugging tools and Java based runtime engines.
- [nonportable](https://github.com/ScoopInstaller/Nonportable) - Non-portable apps (may require UAC)
- [php](https://github.com/ScoopInstaller/PHP) - Installers for most versions of PHP
- [versions](https://github.com/ScoopInstaller/Versions) - Alternative versions of apps found in other buckets
- [main](https://github.com/ScoopInstaller/Main) - Default bucket which contains popular non-GUI apps.
- [extras](https://github.com/ScoopInstaller/Extras) - Apps that do not fit the main bucket's [criteria](https://github.com/ScoopInstaller/Scoop/wiki/Criteria-for-including-apps-in-the-main-bucket).
- [games](https://github.com/Calinou/scoop-games) - Open-source and freeware video games and game-related tools.
- [nerd-fonts](https://github.com/matthewjberger/scoop-nerd-fonts) - Nerd Fonts.
- [nirsoft](https://github.com/ScoopInstaller/Nirsoft) - A collection of over 250+ apps from [Nirsoft](https://nirsoft.net).
- [sysinternals](https://github.com/niheaven/scoop-sysinternals) - The Sysinternals suite from [Microsoft](https://learn.microsoft.com/sysinternals/).
- [java](https://github.com/ScoopInstaller/Java) - A collection of Java development kits (JDKs) and Java runtime engines (JREs), Java's virtual machine debugging tools and Java based runtime engines.
- [nonportable](https://github.com/ScoopInstaller/Nonportable) - Non-portable apps (may trigger UAC prompts).
- [php](https://github.com/ScoopInstaller/PHP) - Installers for most versions of PHP.
- [versions](https://github.com/ScoopInstaller/Versions) - Alternative versions of apps found in other buckets.
The main bucket is installed by default. To add any of the other buckets, type:
The `main` bucket is installed by default. You can make use of more buckets by typing:
```console
scoop bucket add bucketname
scoop bucket add <name>
```
For example, to add the extras bucket, type:
For example, to add the `extras` bucket, type:
```console
scoop bucket add extras
```
You would be able to install apps from the `extras` bucket now.
## Other application buckets
Many other application buckets hosted on Github can be found in the [Scoop Directory](https://rasa.github.io/scoop-directory/) or via [other search engines](https://rasa.github.io/scoop-directory/#other-search-engines).
Many other application buckets hosted on GitHub can be found on [ScoopSearch](https://scoop.sh/) or via [other search engines](https://rasa.github.io/scoop-directory/#other-search-engines).

View File

@@ -46,7 +46,7 @@ param(
. "$PSScriptRoot\..\lib\autoupdate.ps1"
. "$PSScriptRoot\..\lib\json.ps1"
. "$PSScriptRoot\..\lib\versions.ps1"
. "$PSScriptRoot\..\lib\install.ps1"
. "$PSScriptRoot\..\lib\download.ps1"
$Dir = Convert-Path $Dir
if ($ForceUpdate) { $Update = $true }
@@ -121,7 +121,7 @@ foreach ($current in $MANIFESTS) {
Invoke-CachedDownload $current.app $version $_ $null $null -use_cache:$UseCache
$to_check = fullpath (cache_path $current.app $version $_)
$to_check = cache_path $current.app $version $_
$actual_hash = (Get-FileHash -Path $to_check -Algorithm $algorithm).Hash.ToLower()
# Append type of algorithm to both expected and actual if it's not sha256
@@ -146,7 +146,7 @@ foreach ($current in $MANIFESTS) {
Write-Host "$($current.app): " -NoNewline
Write-Host 'Mismatch found ' -ForegroundColor Red
$mismatched | ForEach-Object {
$file = fullpath (cache_path $current.app $version $current.urls[$_])
$file = cache_path $current.app $version $current.urls[$_]
Write-Host "`tURL:`t`t$($current.urls[$_])"
if (Test-Path $file) {
Write-Host "`tFirst bytes:`t$((get_magic_bytes_pretty $file ' ').ToUpper())"

View File

@@ -28,7 +28,7 @@ param(
. "$PSScriptRoot\..\lib\core.ps1"
. "$PSScriptRoot\..\lib\manifest.ps1"
. "$PSScriptRoot\..\lib\install.ps1"
. "$PSScriptRoot\..\lib\download.ps1"
$Dir = Convert-Path $Dir
$Queue = @()

View File

@@ -73,7 +73,7 @@ param(
. "$PSScriptRoot\..\lib\buckets.ps1"
. "$PSScriptRoot\..\lib\json.ps1"
. "$PSScriptRoot\..\lib\versions.ps1"
. "$PSScriptRoot\..\lib\install.ps1" # needed for hash generation
. "$PSScriptRoot\..\lib\download.ps1"
if ($App -ne '*' -and (Test-Path $App -PathType Leaf)) {
$Dir = Split-Path $App
@@ -226,15 +226,21 @@ $Queue | ForEach-Object {
$url = substitute $url $substitutions
$state = New-Object psobject @{
app = $name;
file = $file;
url = $url;
regex = $regex;
json = $json;
jsonpath = $jsonpath;
xpath = $xpath;
reverse = $reverse;
replace = $replace;
app = $name
file = $file
url = $url
regex = $regex
json = $json
jsonpath = $jsonpath
xpath = $xpath
reverse = $reverse
replace = $replace
}
get_config PRIVATE_HOSTS | Where-Object { $_ -ne $null -and $url -match $_.match } | ForEach-Object {
(ConvertFrom-StringData -StringData $_.Headers).GetEnumerator() | ForEach-Object {
$wc.Headers[$_.Key] = $_.Value
}
}
$wc.Headers.Add('Referer', (strip_filename $url))
@@ -254,6 +260,7 @@ while ($in_progress -gt 0) {
$in_progress--
$state = $ev.SourceEventArgs.UserState
$result = $ev.SourceEventArgs.Result
$app = $state.app
$file = $state.file
$json = $state.json
@@ -268,7 +275,7 @@ while ($in_progress -gt 0) {
$ver = $Version
if (!$ver) {
if (!$regex -and $replace) {
if (!$regexp -and $replace) {
next "'replace' requires 're' or 'regex'"
continue
}
@@ -279,15 +286,23 @@ while ($in_progress -gt 0) {
}
if ($url) {
$page = (Get-Encoding($wc)).GetString($ev.SourceEventArgs.Result)
$ms = New-Object System.IO.MemoryStream
$ms.Write($result, 0, $result.Length)
$ms.Seek(0, 0) | Out-Null
if ($result[0] -eq 0x1F -and $result[1] -eq 0x8B) {
$ms = New-Object System.IO.Compression.GZipStream($ms, [System.IO.Compression.CompressionMode]::Decompress)
}
$page = (New-Object System.IO.StreamReader($ms, (Get-Encoding $wc))).ReadToEnd()
}
$source = $url
if ($script) {
$page = Invoke-Command ([scriptblock]::Create($script -join "`r`n"))
$source = 'the output of script'
}
if ($jsonpath) {
# Return only a single value if regex is absent
$noregex = [String]::IsNullOrEmpty($regex)
$noregex = [String]::IsNullOrEmpty($regexp)
# If reverse is ON and regex is ON,
# Then reverse would have no effect because regex handles reverse
# on its own
@@ -297,7 +312,7 @@ while ($in_progress -gt 0) {
$ver = json_path_legacy $page $jsonpath
}
if (!$ver) {
next "couldn't find '$jsonpath' in $url"
next "couldn't find '$jsonpath' in $source"
continue
}
}
@@ -319,7 +334,7 @@ while ($in_progress -gt 0) {
# Getting version from XML, using XPath
$ver = $xml.SelectSingleNode($xpath, $nsmgr).'#text'
if (!$ver) {
next "couldn't find '$($xpath -replace 'ns:', '')' in $url"
next "couldn't find '$($xpath -replace 'ns:', '')' in $source"
continue
}
}
@@ -335,31 +350,31 @@ while ($in_progress -gt 0) {
}
if ($regexp) {
$regex = New-Object System.Text.RegularExpressions.Regex($regexp)
$re = New-Object System.Text.RegularExpressions.Regex($regexp)
if ($reverse) {
$match = $regex.Matches($page) | Select-Object -Last 1
$match = $re.Matches($page) | Select-Object -Last 1
} else {
$match = $regex.Matches($page) | Select-Object -First 1
$match = $re.Matches($page) | Select-Object -First 1
}
if ($match -and $match.Success) {
$matchesHashtable = @{}
$regex.GetGroupNames() | ForEach-Object { $matchesHashtable.Add($_, $match.Groups[$_].Value) }
$re.GetGroupNames() | ForEach-Object { $matchesHashtable.Add($_, $match.Groups[$_].Value) }
$ver = $matchesHashtable['1']
if ($replace) {
$ver = $regex.Replace($match.Value, $replace)
$ver = $re.Replace($match.Value, $replace)
}
if (!$ver) {
$ver = $matchesHashtable['version']
}
} else {
next "couldn't match '$regexp' in $url"
next "couldn't match '$regexp' in $source"
continue
}
}
if (!$ver) {
next "couldn't find new version in $url"
next "couldn't find new version in $source"
continue
}
}

View File

@@ -23,6 +23,7 @@ param(
. "$PSScriptRoot\..\lib\core.ps1"
. "$PSScriptRoot\..\lib\manifest.ps1"
. "$PSScriptRoot\..\lib\description.ps1"
. "$PSScriptRoot\..\lib\download.ps1"
$Dir = Convert-Path $Dir
$Queue = @()

View File

@@ -20,8 +20,8 @@ switch ($subCommand) {
}
({ $subCommand -in @('-v', '--version') }) {
Write-Host 'Current Scoop version:'
if (Test-GitAvailable -and (Test-Path "$PSScriptRoot\..\.git") -and (get_config SCOOP_BRANCH 'master') -ne 'master') {
Invoke-Git -Path "$PSScriptRoot\.." -ArgumentList @('log', 'HEAD', '-1', '--oneline')
if ((Test-GitAvailable) -and (Test-Path "$PSScriptRoot\..\.git") -and ((get_config SCOOP_BRANCH 'master') -ne 'master')) {
Invoke-Git -Path "$PSScriptRoot\.." -ArgumentList @('--no-pager', 'log', 'HEAD', '-1', '--oneline')
} else {
$version = Select-String -Pattern '^## \[(v[\d.]+)\].*?([\d-]+)$' -Path "$PSScriptRoot\..\CHANGELOG.md"
Write-Host $version.Matches.Groups[1].Value -ForegroundColor Cyan -NoNewline
@@ -31,9 +31,9 @@ switch ($subCommand) {
Get-LocalBucket | ForEach-Object {
$bucketLoc = Find-BucketDirectory $_ -Root
if (Test-GitAvailable -and (Test-Path "$bucketLoc\.git")) {
if ((Test-GitAvailable) -and (Test-Path "$bucketLoc\.git")) {
Write-Host "'$_' bucket:"
Invoke-Git -Path $bucketLoc -ArgumentList @('log', 'HEAD', '-1', '--oneline')
Invoke-Git -Path $bucketLoc -ArgumentList @('--no-pager', 'log', 'HEAD', '-1', '--oneline')
Write-Host ''
}
}

View File

@@ -12,6 +12,7 @@ param(
)
. "$PSScriptRoot\..\lib\core.ps1"
. "$PSScriptRoot\..\lib\system.ps1"
. "$PSScriptRoot\..\lib\install.ps1"
. "$PSScriptRoot\..\lib\shortcuts.ps1"
. "$PSScriptRoot\..\lib\versions.ps1"
@@ -41,7 +42,7 @@ function do_uninstall($app, $global) {
$architecture = $install.architecture
Write-Output "Uninstalling '$app'"
run_uninstaller $manifest $architecture $dir
Invoke-Installer -Path $dir -Manifest $manifest -ProcessorArchitecture $architecture -Uninstall
rm_shims $app $manifest $global $architecture
# If a junction was used during install, that will have been used
@@ -98,7 +99,9 @@ if ($purge) {
if ($global) { keep_onlypersist $globaldir }
}
remove_from_path (shimdir $false)
if ($global) { remove_from_path (shimdir $true) }
Remove-Path -Path (shimdir $global) -Global:$global
if (get_config USE_ISOLATED_PATH) {
Remove-Path -Path ('%' + $scoopPathEnvVar + '%') -Global:$global
}
success 'Scoop has been uninstalled.'

View File

@@ -2,7 +2,7 @@
"main": "https://github.com/ScoopInstaller/Main",
"extras": "https://github.com/ScoopInstaller/Extras",
"versions": "https://github.com/ScoopInstaller/Versions",
"nirsoft": "https://github.com/kodybrown/scoop-nirsoft",
"nirsoft": "https://github.com/ScoopInstaller/Nirsoft",
"sysinternals": "https://github.com/niheaven/scoop-sysinternals",
"php": "https://github.com/ScoopInstaller/PHP",
"nerd-fonts": "https://github.com/matthewjberger/scoop-nerd-fonts",

View File

@@ -1,4 +1,24 @@
# Must included with 'json.ps1'
function format_hash([String] $hash) {
$hash = $hash.toLower()
# Workaround for GitHub API:
# `"digest": "sha256:<SHA256_STRING>"`
if ($hash -like 'sha256:*') {
$hash = $hash.Substring(7) # Remove prefix 'sha256:'
}
switch ($hash.Length) {
32 { $hash = "md5:$hash" } # md5
40 { $hash = "sha1:$hash" } # sha1
64 { $hash = $hash } # sha256
128 { $hash = "sha512:$hash" } # sha512
default { $hash = $null }
}
return $hash
}
function find_hash_in_rdf([String] $url, [String] $basename) {
$xml = $null
try {
@@ -37,7 +57,13 @@ function find_hash_in_textfile([String] $url, [Hashtable] $substitutions, [Strin
$wc.Headers.Add('Referer', (strip_filename $url))
$wc.Headers.Add('User-Agent', (Get-UserAgent))
$data = $wc.DownloadData($url)
$hashfile = (Get-Encoding($wc)).GetString($data)
$ms = New-Object System.IO.MemoryStream
$ms.Write($data, 0, $data.Length)
$ms.Seek(0, 0) | Out-Null
if ($data[0] -eq 0x1F -and $data[1] -eq 0x8B) {
$ms = New-Object System.IO.Compression.GZipStream($ms, [System.IO.Compression.CompressionMode]::Decompress)
}
$hashfile = (New-Object System.IO.StreamReader($ms, (Get-Encoding $wc))).ReadToEnd()
} catch [system.net.webexception] {
Write-Host $_ -ForegroundColor DarkRed
Write-Host "URL $url is not valid" -ForegroundColor DarkRed
@@ -93,7 +119,13 @@ function find_hash_in_json([String] $url, [Hashtable] $substitutions, [String] $
$wc.Headers.Add('Referer', (strip_filename $url))
$wc.Headers.Add('User-Agent', (Get-UserAgent))
$data = $wc.DownloadData($url)
$json = (Get-Encoding($wc)).GetString($data)
$ms = New-Object System.IO.MemoryStream
$ms.Write($data, 0, $data.Length)
$ms.Seek(0, 0) | Out-Null
if ($data[0] -eq 0x1F -and $data[1] -eq 0x8B) {
$ms = New-Object System.IO.Compression.GZipStream($ms, [System.IO.Compression.CompressionMode]::Decompress)
}
$json = (New-Object System.IO.StreamReader($ms, (Get-Encoding $wc))).ReadToEnd()
} catch [System.Net.WebException] {
Write-Host $_ -ForegroundColor DarkRed
Write-Host "URL $url is not valid" -ForegroundColor DarkRed
@@ -115,7 +147,13 @@ function find_hash_in_xml([String] $url, [Hashtable] $substitutions, [String] $x
$wc.Headers.Add('Referer', (strip_filename $url))
$wc.Headers.Add('User-Agent', (Get-UserAgent))
$data = $wc.DownloadData($url)
$xml = [xml]((Get-Encoding($wc)).GetString($data))
$ms = New-Object System.IO.MemoryStream
$ms.Write($data, 0, $data.Length)
$ms.Seek(0, 0) | Out-Null
if ($data[0] -eq 0x1F -and $data[1] -eq 0x8B) {
$ms = New-Object System.IO.Compression.GZipStream($ms, [System.IO.Compression.CompressionMode]::Decompress)
}
$xml = [xml]((New-Object System.IO.StreamReader($ms, (Get-Encoding $wc))).ReadToEnd())
} catch [system.net.webexception] {
Write-Host $_ -ForegroundColor DarkRed
Write-Host "URL $url is not valid" -ForegroundColor DarkRed
@@ -173,13 +211,14 @@ function get_hash_for_app([String] $app, $config, [String] $version, [String] $u
$hash = $null
$hashmode = $config.mode
$originurl = strip_fragment $url
$basename = [System.Web.HttpUtility]::UrlDecode((url_remote_filename($url)))
$substitutions = $substitutions.Clone()
$substitutions.Add('$url', (strip_fragment $url))
$substitutions.Add('$baseurl', (strip_filename (strip_fragment $url)).TrimEnd('/'))
$substitutions.Add('$url', $originurl)
$substitutions.Add('$baseurl', (strip_filename $originurl).TrimEnd('/'))
$substitutions.Add('$basename', $basename)
$substitutions.Add('$urlNoExt', (strip_ext (strip_fragment $url)))
$substitutions.Add('$urlNoExt', (strip_ext $originurl))
$substitutions.Add('$basenameNoExt', (strip_ext $basename))
debug $substitutions
@@ -228,6 +267,10 @@ function get_hash_for_app([String] $app, $config, [String] $version, [String] $u
$hashmode = 'sourceforge'
}
if (!$hashfile_url -and $url -match 'https:\/\/github\.com\/(?<owner>[^\/]+)\/(?<repo>[^\/]+)\/releases\/download\/[^\/]+\/[^\/]+') {
$hashmode = 'github'
}
switch ($hashmode) {
'extract' {
$hash = find_hash_in_textfile $hashfile_url $substitutions $regex
@@ -255,6 +298,10 @@ function get_hash_for_app([String] $app, $config, [String] $version, [String] $u
$hashfile_url = (strip_filename (strip_fragment "https://sourceforge.net/projects/$($matches['project'])/files/$($matches['file'])")).TrimEnd('/')
$hash = find_hash_in_textfile $hashfile_url $substitutions '"$basename":.*?"sha1":\s*"([a-fA-F0-9]{40})"'
}
'github' {
$hashfile_url = "https://api.github.com/repos/$($matches['owner'])/$($matches['repo'])/releases"
$hash = find_hash_in_json $hashfile_url $substitutions ("$..assets[?(@.browser_download_url == '" + $originurl + "')].digest")
}
}
if ($hash) {
@@ -278,7 +325,7 @@ function get_hash_for_app([String] $app, $config, [String] $version, [String] $u
Write-Host "URL $url is not valid" -ForegroundColor DarkRed
return $null
}
$file = fullpath (cache_path $app $version $url)
$file = cache_path $app $version $url
$hash = (Get-FileHash -Path $file -Algorithm SHA256).Hash.ToLower()
Write-Host 'Computed hash: ' -ForegroundColor DarkYellow -NoNewline
Write-Host $hash -ForegroundColor Green

View File

@@ -58,10 +58,18 @@ function Get-LocalBucket {
.SYNOPSIS
List all local buckets.
#>
$bucketNames = (Get-ChildItem -Path $bucketsdir -Directory).Name
$bucketNames = [System.Collections.Generic.List[String]](Get-ChildItem -Path $bucketsdir -Directory).Name
if ($null -eq $bucketNames) {
return @() # Return a zero-length list instead of $null.
} else {
$knownBuckets = known_buckets
for ($i = $knownBuckets.Count - 1; $i -ge 0 ; $i--) {
$name = $knownBuckets[$i]
if ($bucketNames.Contains($name)) {
[void]$bucketNames.Remove($name)
$bucketNames.Insert(0, $name)
}
}
return $bucketNames
}
}
@@ -100,10 +108,10 @@ function list_buckets {
$path = Find-BucketDirectory $_ -Root
if ((Test-Path (Join-Path $path '.git')) -and (Get-Command git -ErrorAction SilentlyContinue)) {
$bucket.Source = Invoke-Git -Path $path -ArgumentList @('config', 'remote.origin.url')
$bucket.Updated = Invoke-Git -Path $path -ArgumentList @('log', "--format='%aD'", '-n', '1')
$bucket.Updated = Invoke-Git -Path $path -ArgumentList @('log', '--format=%aI', '-n', '1') | Get-Date
} else {
$bucket.Source = friendly_path $path
$bucket.Updated = (Get-Item "$path\bucket").LastWriteTime
$bucket.Updated = (Get-Item "$path\bucket" -ErrorAction SilentlyContinue).LastWriteTime
}
$bucket.Manifests = Get-ChildItem "$path\bucket" -Force -Recurse -ErrorAction SilentlyContinue |
Measure-Object | Select-Object -ExpandProperty Count
@@ -146,8 +154,17 @@ function add_bucket($name, $repo) {
}
ensure $bucketsdir | Out-Null
$dir = ensure $dir
Invoke-Git -ArgumentList @('clone', $repo, $dir, '-q')
$out = Invoke-Git -ArgumentList @('clone', $repo, $dir, '-q')
if ($LASTEXITCODE -ne 0) {
error "Failed to clone '$repo' to '$dir'.`n`nError given:`n$out`n`nPlease check the repository URL or network connection and try again."
Remove-Item $dir -Recurse -Force -ErrorAction SilentlyContinue
return 1
}
Write-Host 'OK'
if (get_config USE_SQLITE_CACHE) {
info 'Updating cache...'
Set-ScoopDB -Path (Get-ChildItem (Find-BucketDirectory $name) -Filter '*.json' -Recurse).FullName
}
success "The $name bucket was added successfully."
return 0
}
@@ -160,6 +177,11 @@ function rm_bucket($name) {
}
Remove-Item $dir -Recurse -Force -ErrorAction Stop
if (get_config USE_SQLITE_CACHE) {
info 'Updating cache...'
Remove-ScoopDBItem -Bucket $name
}
success "The $name bucket was removed successfully."
return 0
}

View File

@@ -1,6 +1,10 @@
# Description: Functions for managing commands and aliases.
## Functions for commands
function command_files {
(Get-ChildItem "$PSScriptRoot\..\libexec") + (Get-ChildItem "$scoopdir\shims") |
Where-Object 'scoop-.*?\.ps1$' -Property Name -Match
Where-Object 'scoop-.*?\.ps1$' -Property Name -Match
}
function commands {
@@ -19,11 +23,10 @@ function command_path($cmd) {
# get path from shim
$shim_path = "$scoopdir\shims\scoop-$cmd.ps1"
$line = ((Get-Content $shim_path) | Where-Object { $_.startswith('$path') })
if($line) {
if ($line) {
Invoke-Command ([scriptblock]::Create($line)) -NoNewScope
$cmd_path = $path
}
else { $cmd_path = $shim_path }
} else { $cmd_path = $shim_path }
}
$cmd_path
@@ -34,3 +37,92 @@ function exec($cmd, $arguments) {
& $cmd_path @arguments
}
## Functions for aliases
function add_alias {
param(
[ValidateNotNullOrEmpty()]
[string]$name,
[ValidateNotNullOrEmpty()]
[string]$command,
[string]$description
)
$aliases = get_config ALIAS ([PSCustomObject]@{})
if ($aliases.$name) {
abort "Alias '$name' already exists."
}
$alias_script_name = "scoop-$name"
$shimdir = shimdir $false
if (Test-Path "$shimdir\$alias_script_name.ps1") {
abort "File '$alias_script_name.ps1' already exists in shims directory."
}
$script = @(
"# Summary: $description",
"$command"
) -join "`n"
try {
$script | Out-UTF8File "$shimdir\$alias_script_name.ps1"
} catch {
abort $_.Exception
}
# Add the new alias to the config.
$aliases | Add-Member -MemberType NoteProperty -Name $name -Value $alias_script_name
set_config ALIAS $aliases | Out-Null
}
function rm_alias {
param(
[ValidateNotNullOrEmpty()]
[string]$name
)
$aliases = get_config ALIAS ([PSCustomObject]@{})
if (!$aliases.$name) {
abort "Alias '$name' doesn't exist."
}
info "Removing alias '$name'..."
if (Test-Path "$(shimdir $false)\scoop-$name.ps1") {
Remove-Item "$(shimdir $false)\scoop-$name.ps1"
}
$aliases.PSObject.Properties.Remove($name)
set_config ALIAS $aliases | Out-Null
}
function list_aliases {
param(
[bool]$verbose
)
$aliases = get_config ALIAS ([PSCustomObject]@{})
$alias_info = $aliases.PSObject.Properties.Name | Where-Object { $_ } | ForEach-Object {
# Mark the alias as <BROKEN>, if the alias script file does NOT exist.
if (!(Test-Path "$(shimdir $false)\scoop-$_.ps1")) {
[PSCustomObject]@{
Name = $_
Command = '<BROKEN>'
}
return
}
$content = Get-Content (command_path $_)
[PSCustomObject]@{
Name = $_
Command = ($content | Select-Object -Skip 1).Trim()
Summary = (summary $content).Trim()
}
}
if (!$alias_info) {
info 'No alias found.'
return
}
$alias_info = $alias_info | Sort-Object Name
$properties = @('Name', 'Command')
if ($verbose) {
$properties += 'Summary'
}
$alias_info | Select-Object $properties
}

File diff suppressed because it is too large Load Diff

390
lib/database.ps1 Normal file
View File

@@ -0,0 +1,390 @@
# Description: Functions for interacting with the Scoop database cache
<#
.SYNOPSIS
Get SQLite .NET driver
.DESCRIPTION
Download and extract the SQLite .NET driver from NuGet.
.PARAMETER Version
System.String
The version of the SQLite .NET driver to download.
.INPUTS
None
.OUTPUTS
System.Boolean
True if the SQLite .NET driver was successfully downloaded and extracted, otherwise false.
#>
function Get-SQLite {
param (
[string]$Version = '1.0.118'
)
# Install SQLite
try {
Write-Host "Downloading SQLite $Version..." -ForegroundColor DarkYellow
$sqlitePkgPath = "$env:TEMP\sqlite.zip"
$sqliteTempPath = "$env:TEMP\sqlite"
$sqlitePath = "$PSScriptRoot\..\supporting\sqlite"
Invoke-WebRequest -Uri "https://api.nuget.org/v3-flatcontainer/stub.system.data.sqlite.core.netframework/$version/stub.system.data.sqlite.core.netframework.$version.nupkg" -OutFile $sqlitePkgPath
Write-Host "Extracting SQLite $Version..." -ForegroundColor DarkYellow -NoNewline
Expand-Archive -Path $sqlitePkgPath -DestinationPath $sqliteTempPath -Force
New-Item -Path $sqlitePath -ItemType Directory -Force | Out-Null
Move-Item -Path "$sqliteTempPath\build\net451\*", "$sqliteTempPath\lib\net451\System.Data.SQLite.dll" -Destination $sqlitePath -Force
Remove-Item -Path $sqlitePkgPath, $sqliteTempPath -Recurse -Force
Write-Host ' Done' -ForegroundColor DarkYellow
return $true
} catch {
return $false
}
}
<#
.SYNOPSIS
Open Scoop SQLite database.
.DESCRIPTION
Open Scoop SQLite database connection and create the necessary tables if not exists.
.INPUTS
None
.OUTPUTS
System.Data.SQLite.SQLiteConnection
The SQLite database connection if **PassThru** is used.
#>
function Open-ScoopDB {
# Load System.Data.SQLite
if (!('System.Data.SQLite.SQLiteConnection' -as [Type])) {
try {
if (!(Test-Path -Path "$PSScriptRoot\..\supporting\sqlite\System.Data.SQLite.dll")) {
Get-SQLite | Out-Null
}
Add-Type -Path "$PSScriptRoot\..\supporting\sqlite\System.Data.SQLite.dll"
} catch {
throw "Scoop's Database cache requires the ADO.NET driver:`n`thttp://system.data.sqlite.org/index.html/doc/trunk/www/downloads.wiki"
}
}
$dbPath = Join-Path $scoopdir 'scoop.db'
$db = New-Object -TypeName System.Data.SQLite.SQLiteConnection
$db.ConnectionString = "Data Source=$dbPath"
$db.ParseViaFramework = $true # Allow UNC path
$db.Open()
$tableCommand = $db.CreateCommand()
$tableCommand.CommandText = "CREATE TABLE IF NOT EXISTS 'app' (
name TEXT NOT NULL COLLATE NOCASE,
description TEXT NOT NULL,
version TEXT NOT NULL,
bucket VARCHAR NOT NULL,
manifest JSON NOT NULL,
binary TEXT,
shortcut TEXT,
dependency TEXT,
suggest TEXT,
PRIMARY KEY (name, version, bucket)
)"
$tableCommand.CommandType = [System.Data.CommandType]::Text
$tableCommand.ExecuteNonQuery() | Out-Null
$tableCommand.Dispose()
return $db
}
<#
.SYNOPSIS
Set Scoop database item(s).
.DESCRIPTION
Insert or replace item(s) into the Scoop SQLite database.
.PARAMETER InputObject
System.Object[]
The database item(s) to insert or replace.
.INPUTS
System.Object[]
.OUTPUTS
None
#>
function Set-ScoopDBItem {
[CmdletBinding()]
param (
[Parameter(Mandatory, Position = 0, ValueFromPipeline)]
[psobject[]]
$InputObject
)
begin {
$db = Open-ScoopDB
$dbTrans = $db.BeginTransaction()
# TODO Support [hashtable]$InputObject
$colName = @($InputObject | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name)
$dbQuery = "INSERT OR REPLACE INTO app ($($colName -join ', ')) VALUES ($('@' + ($colName -join ', @')))"
$dbCommand = $db.CreateCommand()
$dbCommand.CommandText = $dbQuery
$dbCommand.CommandType = [System.Data.CommandType]::Text
}
process {
foreach ($item in $InputObject) {
$item.PSObject.Properties | ForEach-Object {
$dbCommand.Parameters.AddWithValue("@$($_.Name)", $_.Value) | Out-Null
}
$dbCommand.ExecuteNonQuery() | Out-Null
}
}
end {
try {
$dbTrans.Commit()
} catch {
$dbTrans.Rollback()
throw $_
} finally {
$dbCommand.Dispose()
$dbTrans.Dispose()
$db.Dispose()
}
}
}
<#
.SYNOPSIS
Set Scoop app database item(s).
.DESCRIPTION
Insert or replace Scoop app(s) into the database.
.PARAMETER Path
System.String
The path to the bucket.
.PARAMETER CommitHash
System.String
The commit hash to compare with the HEAD.
.INPUTS
None
.OUTPUTS
None
#>
function Set-ScoopDB {
[CmdletBinding()]
param (
[Parameter(Position = 0, ValueFromPipeline)]
[string[]]
$Path
)
begin {
$list = [System.Collections.Generic.List[psobject]]::new()
$arch = Get-DefaultArchitecture
}
process {
if ($Path.Count -eq 0) {
$bucketPath = Get-LocalBucket | ForEach-Object { Find-BucketDirectory $_ }
$Path = (Get-ChildItem $bucketPath -Filter '*.json' -Recurse).FullName
}
$Path | ForEach-Object {
$manifestRaw = [System.IO.File]::ReadAllText($_)
$manifest = ConvertFrom-Json $manifestRaw -ErrorAction SilentlyContinue
if ($null -ne $manifest.version) {
$list.Add([pscustomobject]@{
name = $($_ -replace '.*[\\/]([^\\/]+)\.json$', '$1')
description = if ($manifest.description) { $manifest.description } else { '' }
version = $manifest.version
bucket = $($_ -replace '.*buckets[\\/]([^\\/]+)(?:[\\/].*)', '$1')
manifest = $manifestRaw
binary = $(
$result = @()
@(arch_specific 'bin' $manifest $arch) | ForEach-Object {
if ($_ -is [System.Array]) {
$result += "$($_[1]).$($_[0].Split('.')[-1])"
} else {
$result += $_
}
}
$result -replace '.*?([^\\/]+)?(\.(exe|bat|cmd|ps1|jar|py))$', '$1' -join ' | '
)
shortcut = $(
$result = @()
@(arch_specific 'shortcuts' $manifest $arch) | ForEach-Object {
$result += $_[1]
}
$result -replace '.*?([^\\/]+$)', '$1' -join ' | '
)
dependency = $manifest.depends -join ' | '
suggest = $(
$suggest_output = @()
$manifest.suggest.PSObject.Properties | ForEach-Object {
$suggest_output += $_.Value -join ' | '
}
$suggest_output -join ' | '
)
})
}
}
}
end {
if ($list.Count -ne 0) {
Set-ScoopDBItem $list
}
}
}
<#
.SYNOPSIS
Select Scoop database item(s).
.DESCRIPTION
Select item(s) from the Scoop SQLite database.
The pattern is matched against the name, binaries, and shortcuts columns for apps.
.PARAMETER Pattern
System.String
The pattern to search for. If is an empty string, all items will be returned.
.PARAMETER From
System.String[]
The fields to search from.
.INPUTS
System.String
.OUTPUTS
System.Data.DataTable
The selected database item(s).
#>
function Select-ScoopDBItem {
[CmdletBinding()]
param (
[Parameter(Mandatory, Position = 0, ValueFromPipeline)]
[AllowEmptyString()]
[string]
$Pattern,
[Parameter(Mandatory, Position = 1)]
[ValidateNotNullOrEmpty()]
[string[]]
$From
)
begin {
$db = Open-ScoopDB
$dbAdapter = New-Object -TypeName System.Data.SQLite.SQLiteDataAdapter
$result = New-Object System.Data.DataTable
$dbQuery = "SELECT * FROM app WHERE $(($From -join ' LIKE @Pattern OR ') + ' LIKE @Pattern')"
$dbQuery = "SELECT * FROM ($($dbQuery + ' ORDER BY version DESC')) GROUP BY name, bucket"
$dbCommand = $db.CreateCommand()
$dbCommand.CommandText = $dbQuery
$dbCommand.CommandType = [System.Data.CommandType]::Text
$dbAdapter.SelectCommand = $dbCommand
}
process {
$dbCommand.Parameters.AddWithValue('@Pattern', $(if ($Pattern -eq '') { '%' } else { '%' + $Pattern + '%' })) | Out-Null
[void]$dbAdapter.Fill($result)
}
end {
$dbAdapter.Dispose()
$db.Dispose()
return $result
}
}
<#
.SYNOPSIS
Get Scoop database item.
.DESCRIPTION
Get item from the Scoop SQLite database.
.PARAMETER Name
System.String
The name of the item to get.
.PARAMETER Bucket
System.String
The bucket of the item to get.
.PARAMETER Version
System.String
The version of the item to get. If not provided, the latest version will be returned.
.INPUTS
System.String
.OUTPUTS
System.Data.DataTable
The selected database item.
#>
function Get-ScoopDBItem {
[CmdletBinding()]
param (
[Parameter(Mandatory, Position = 0, ValueFromPipeline)]
[string]
$Name,
[Parameter(Mandatory, Position = 1)]
[string]
$Bucket,
[Parameter(Position = 2)]
[string]
$Version
)
begin {
$db = Open-ScoopDB
$dbAdapter = New-Object -TypeName System.Data.SQLite.SQLiteDataAdapter
$result = New-Object System.Data.DataTable
$dbQuery = 'SELECT * FROM app WHERE name = @Name AND bucket = @Bucket'
if ($Version) {
$dbQuery += ' AND version = @Version'
} else {
$dbQuery += ' ORDER BY version DESC LIMIT 1'
}
$dbCommand = $db.CreateCommand()
$dbCommand.CommandText = $dbQuery
$dbCommand.CommandType = [System.Data.CommandType]::Text
$dbAdapter.SelectCommand = $dbCommand
}
process {
$dbCommand.Parameters.AddWithValue('@Name', $Name) | Out-Null
$dbCommand.Parameters.AddWithValue('@Bucket', $Bucket) | Out-Null
$dbCommand.Parameters.AddWithValue('@Version', $Version) | Out-Null
[void]$dbAdapter.Fill($result)
}
end {
$dbAdapter.Dispose()
$db.Dispose()
return $result
}
}
<#
.SYNOPSIS
Remove Scoop database item(s).
.DESCRIPTION
Remove item(s) from the Scoop SQLite database.
.PARAMETER Name
System.String
The name of the item to remove.
.PARAMETER Bucket
System.String
The bucket of the item to remove.
.INPUTS
System.String
.OUTPUTS
None
#>
function Remove-ScoopDBItem {
[CmdletBinding()]
param (
[Parameter(Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName)]
[string]
$Name,
[Parameter(Mandatory, Position = 1, ValueFromPipelineByPropertyName)]
[string]
$Bucket
)
begin {
$db = Open-ScoopDB
$dbTrans = $db.BeginTransaction()
$dbQuery = 'DELETE FROM app WHERE bucket = @Bucket'
$dbCommand = $db.CreateCommand()
$dbCommand.CommandText = $dbQuery
$dbCommand.CommandType = [System.Data.CommandType]::Text
}
process {
$dbCommand.Parameters.AddWithValue('@Bucket', $Bucket) | Out-Null
if ($Name) {
$dbCommand.CommandText = $dbQuery + ' AND name = @Name'
$dbCommand.Parameters.AddWithValue('@Name', $Name) | Out-Null
}
$dbCommand.ExecuteNonQuery() | Out-Null
}
end {
try {
$dbTrans.Commit()
} catch {
$dbTrans.Rollback()
throw $_
} finally {
$dbCommand.Dispose()
$dbTrans.Dispose()
$db.Dispose()
}
}
}

View File

@@ -1,3 +1,67 @@
# Description: Functions for decompressing archives or installers
function Invoke-Extraction {
param (
[string]
$Path,
[string[]]
$Name,
[psobject]
$Manifest,
[Alias('Arch', 'Architecture')]
[string]
$ProcessorArchitecture
)
$uri = @(url $Manifest $ProcessorArchitecture)
# 'extract_dir' and 'extract_to' are paired
$extractDir = @(extract_dir $Manifest $ProcessorArchitecture)
$extractTo = @(extract_to $Manifest $ProcessorArchitecture)
$extracted = 0
for ($i = 0; $i -lt $Name.Length; $i++) {
# work out extraction method, if applicable
$extractFn = $null
switch -regex ($Name[$i]) {
'\.zip$' {
if ((Test-HelperInstalled -Helper 7zip) -or ((get_config USE_EXTERNAL_7ZIP) -and (Test-CommandAvailable 7z))) {
$extractFn = 'Expand-7zipArchive'
} else {
$extractFn = 'Expand-ZipArchive'
}
continue
}
'\.msi$' {
$extractFn = 'Expand-MsiArchive'
continue
}
'\.exe$' {
if ($Manifest.innosetup) {
$extractFn = 'Expand-InnoArchive'
}
continue
}
{ Test-7zipRequirement -Uri $_ } {
$extractFn = 'Expand-7zipArchive'
continue
}
}
if ($extractFn) {
$fnArgs = @{
Path = Join-Path $Path $Name[$i]
DestinationPath = Join-Path $Path $extractTo[$extracted]
ExtractDir = $extractDir[$extracted]
}
Write-Host 'Extracting ' -NoNewline
Write-Host $(url_remote_filename $uri[$i]) -ForegroundColor Cyan -NoNewline
Write-Host ' ... ' -NoNewline
& $extractFn @fnArgs -Removal
Write-Host 'done.' -ForegroundColor Green
$extracted++
}
}
}
function Expand-7zipArchive {
[CmdletBinding()]
param (
@@ -46,12 +110,6 @@ function Expand-7zipArchive {
if (!$Status) {
abort "Failed to extract files from $Path.`nLog file:`n $(friendly_path $LogPath)`n$(new_issue_msg $app $bucket 'decompress error')"
}
if (!$IsTar -and $ExtractDir) {
movedir "$DestinationPath\$ExtractDir" $DestinationPath | Out-Null
}
if (Test-Path $LogPath) {
Remove-Item $LogPath -Force
}
if ($IsTar) {
# Check for tar
$Status = Invoke-ExternalCommand $7zPath @('l', $Path) -LogPath $LogPath
@@ -63,15 +121,26 @@ function Expand-7zipArchive {
abort "Failed to list files in $Path.`nNot a 7-Zip supported archive file."
}
}
if (!$IsTar -and $ExtractDir) {
movedir "$DestinationPath\$ExtractDir" $DestinationPath | Out-Null
# Remove temporary directory if it is empty
$ExtractDirTopPath = [string] "$DestinationPath\$($ExtractDir -replace '[\\/].*')"
if ((Get-ChildItem -Path $ExtractDirTopPath -Force -ErrorAction Ignore).Count -eq 0) {
Remove-Item -Path $ExtractDirTopPath -Recurse -Force -ErrorAction Ignore
}
}
if (Test-Path $LogPath) {
Remove-Item $LogPath -Force
}
if ($Removal) {
# Remove original archive file
if (($Path -replace '.*\.([^\.]*)$', '$1') -eq '001') {
# Remove splited 7-zip archive parts
# Remove splitted 7-zip archive parts
Get-ChildItem "$($Path -replace '\.[^\.]*$', '').???" | Remove-Item -Force
} elseif (($Path -replace '.*\.part(\d+)\.rar$', '$1')[-1] -eq '1') {
# Remove splitted RAR archive parts
Get-ChildItem "$($Path -replace '\.part(\d+)\.rar$', '').part*.rar" | Remove-Item -Force
} else {
# Remove original archive file
Remove-Item $Path -Force
}
}
@@ -94,35 +163,9 @@ function Expand-ZstdArchive {
[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
}
# TODO: Remove this function after 2024/12/31
Show-DeprecatedWarning $MyInvocation 'Expand-7zipArchive'
Expand-7zipArchive -Path $Path -DestinationPath $DestinationPath -ExtractDir $ExtractDir -Switches $Switches -Removal:$Removal
}
function Expand-MsiArchive {
@@ -152,7 +195,7 @@ function Expand-MsiArchive {
$ArgList = @('x', $Path, "$DestinationPath\")
} else {
$MsiPath = 'msiexec.exe'
$ArgList = @('/a', "`"$Path`"", '/qn', "TARGETDIR=`"$DestinationPath\SourceDir`"")
$ArgList = @('/a', $Path, '/qn', "TARGETDIR=$DestinationPath\SourceDir")
}
$LogPath = "$(Split-Path $Path)\msi.log"
if ($Switches) {
@@ -241,8 +284,14 @@ function Expand-ZipArchive {
$OriDestinationPath = $DestinationPath
$DestinationPath = "$DestinationPath\_tmp"
}
# Disable progress bar to gain performance
$oldProgressPreference = $ProgressPreference
$global:ProgressPreference = 'SilentlyContinue'
# Compatible with Pscx v3 (https://github.com/Pscx/Pscx) ('Microsoft.PowerShell.Archive' is not needed for Pscx v4)
Microsoft.PowerShell.Archive\Expand-Archive -Path $Path -DestinationPath $DestinationPath -Force
$global:ProgressPreference = $oldProgressPreference
if ($ExtractDir) {
movedir "$DestinationPath\$ExtractDir" $OriDestinationPath | Out-Null
Remove-Item $DestinationPath -Recurse -Force
@@ -269,14 +318,32 @@ function Expand-DarkArchive {
$Removal
)
$LogPath = "$(Split-Path $Path)\dark.log"
$ArgList = @('-nologo', '-x', $DestinationPath, $Path)
$DarkPath = Get-HelperPath -Helper Dark
if ((Split-Path $DarkPath -Leaf) -eq 'wix.exe') {
$ArgList = @('burn', 'extract', $Path, '-out', $DestinationPath, '-outba', "$DestinationPath\UX")
} else {
$ArgList = @('-nologo', '-x', $DestinationPath, $Path)
}
if ($Switches) {
$ArgList += (-split $Switches)
}
$Status = Invoke-ExternalCommand (Get-HelperPath -Helper Dark) $ArgList -LogPath $LogPath
$Status = Invoke-ExternalCommand $DarkPath $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')"
}
if (Test-Path "$DestinationPath\WixAttachedContainer") {
Rename-Item "$DestinationPath\WixAttachedContainer" 'AttachedContainer' -ErrorAction Ignore
} else {
if (Test-Path "$DestinationPath\AttachedContainer\a0") {
$Xml = [xml](Get-Content -Raw "$DestinationPath\UX\manifest.xml" -Encoding utf8)
$Xml.BurnManifest.UX.Payload | ForEach-Object {
Rename-Item "$DestinationPath\UX\$($_.SourcePath)" $_.FilePath -ErrorAction Ignore
}
$Xml.BurnManifest.Payload | ForEach-Object {
Rename-Item "$DestinationPath\AttachedContainer\$($_.SourcePath)" $_.FilePath -ErrorAction Ignore
}
}
}
if (Test-Path $LogPath) {
Remove-Item $LogPath -Force
}

View File

@@ -37,9 +37,9 @@ function Get-Dependency {
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>'."
warn "Bucket '$bucket' not added. Add it with $(if($bucket -in (known_buckets)) { "'scoop bucket add $bucket' or " })'scoop bucket add $bucket <repo>'."
}
abort "Couldn't find manifest for '$AppName'$(if(!$bucket) { '.' } else { " from '$bucket' bucket." })"
abort "Couldn't find manifest for '$AppName'$(if($bucket) { " from '$bucket' bucket" } elseif($url) { " at '$url'" })."
}
$deps = @(Get-InstallationHelper $manifest $Architecture) + @($manifest.depends) | Select-Object -Unique
@@ -118,11 +118,8 @@ function Get-InstallationHelper {
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 {
'7zip', 'lessmsi', 'innounp', 'dark' | ForEach-Object {
if (Test-HelperInstalled -Helper $_) {
$helper = $helper -ne $_
}
@@ -144,22 +141,10 @@ function Test-7zipRequirement {
$Uri
)
return ($Uri | Where-Object {
$_ -match '\.((gz)|(tar)|(t[abgpx]z2?)|(lzma)|(bz2?)|(7z)|(001)|(rar)|(iso)|(xz)|(lzh)|(nupkg))(\.[^\d.]+)?$'
$_ -match '\.(001|7z|bz(ip)?2?|gz|img|iso|lzma|lzh|nupkg|rar|tar|t[abgpx]z2?|t?zst|xz)(\.[^\d.]+)?$'
}).Count -gt 0
}
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])]

769
lib/download.ps1 Normal file
View File

@@ -0,0 +1,769 @@
# Description: Functions for downloading files
## Meta downloader
function Invoke-ScoopDownload ($app, $version, $manifest, $bucket, $architecture, $dir, $use_cache = $true, $check_hash = $true) {
# we only want to show this warning once
if (!$use_cache) { warn 'Cache is being ignored.' }
# can be multiple urls: if there are, then installer should go first to make 'installer.args' section work
$urls = @(script:url $manifest $architecture)
# can be multiple cookies: they will be used for all HTTP requests.
$cookies = $manifest.cookie
# download first
if (Test-Aria2Enabled) {
Invoke-CachedAria2Download $app $version $manifest $architecture $dir $cookies $use_cache $check_hash
} else {
foreach ($url in $urls) {
$fname = url_filename $url
try {
Invoke-CachedDownload $app $version $url "$dir\$fname" $cookies $use_cache
} catch {
Write-Host -ForegroundColor DarkRed $_
abort "URL $url is not valid"
}
if ($check_hash) {
$manifest_hash = hash_for_url $manifest $url $architecture
$ok, $err = check_hash "$dir\$fname" $manifest_hash $(show_app $app $bucket)
if (!$ok) {
error $err
$cached = cache_path $app $version $url
if (Test-Path $cached) {
# rm cached file
Remove-Item -Force $cached
}
if ($url.Contains('sourceforge.net')) {
Write-Host -ForegroundColor 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')
}
}
}
}
return $urls.ForEach({ url_filename $_ })
}
## [System.Net] downloader
function Invoke-CachedDownload ($app, $version, $url, $to, $cookies = $null, $use_cache = $true) {
$cached = cache_path $app $version $url
if (!(Test-Path $cached) -or !$use_cache) {
ensure $cachedir | Out-Null
Start-Download $url "$cached.download" $cookies
Move-Item "$cached.download" $cached -Force
} else { Write-Host "Loading $(url_remote_filename $url) from cache" }
if (!($null -eq $to)) {
if ($use_cache) {
Copy-Item $cached $to
} else {
Move-Item $cached $to -Force
}
}
}
function Start-Download ($url, $to, $cookies) {
$progress = [console]::isoutputredirected -eq $false -and
$host.name -ne 'Windows PowerShell ISE Host'
try {
$url = handle_special_urls $url
Invoke-Download $url $to $cookies $progress
} catch {
$e = $_.exception
if ($e.Response.StatusCode -eq 'Unauthorized') {
warn 'Token might be misconfigured.'
}
if ($e.innerexception) { $e = $e.innerexception }
throw $e
}
}
function Invoke-Download ($url, $to, $cookies, $progress) {
# download with filesize and progress indicator
$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 ($url -match 'api\.github\.com/repos') {
$wreq.Accept = 'application/octet-stream'
$wreq.Headers['Authorization'] = "Bearer $(Get-GitHubToken)"
$wreq.Headers['X-GitHub-Api-Version'] = '2022-11-28'
}
if ($cookies) {
$wreq.Headers.Add('Cookie', (cookie_header $cookies))
}
get_config PRIVATE_HOSTS | Where-Object { $_ -ne $null -and $url -match $_.match } | ForEach-Object {
(ConvertFrom-StringData -StringData $_.Headers).GetEnumerator() | ForEach-Object {
$wreq.Headers[$_.Key] = $_.Value
}
}
}
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"
}
Invoke-Download $newUrl $to $cookies $progress
return
}
$total = $wres.ContentLength
if ($total -eq -1 -and $wreq -is [net.ftpwebrequest]) {
$total = ftp_file_size($url)
}
if ($progress -and ($total -gt 0)) {
[console]::CursorVisible = $false
function Trace-DownloadProgress ($read) {
Write-DownloadProgress $read $total $url
}
} else {
Write-Host "Downloading $url ($(filesize $total))..."
function Trace-DownloadProgress {
#no op
}
}
try {
$s = $wres.getresponsestream()
$fs = [io.file]::openwrite($to)
$buffer = New-Object byte[] 2048
$totalRead = 0
$sw = [diagnostics.stopwatch]::StartNew()
Trace-DownloadProgress $totalRead
while (($read = $s.read($buffer, 0, $buffer.length)) -gt 0) {
$fs.write($buffer, 0, $read)
$totalRead += $read
if ($sw.elapsedmilliseconds -gt 100) {
$sw.restart()
Trace-DownloadProgress $totalRead
}
}
$sw.stop()
Trace-DownloadProgress $totalRead
} finally {
if ($progress) {
[console]::CursorVisible = $true
Write-Host
}
if ($fs) {
$fs.close()
}
if ($s) {
$s.close()
}
$wres.close()
}
}
function Format-DownloadProgress ($url, $read, $total, $console) {
$filename = url_remote_filename $url
# calculate current percentage done
$p = [math]::Round($read / $total * 100, 0)
# pre-generate LHS and RHS of progress string
# so we know how much space we have
$left = "$filename ($(filesize $total))"
$right = [string]::Format('{0,3}%', $p)
# calculate remaining width for progress bar
$midwidth = $console.BufferSize.Width - ($left.Length + $right.Length + 8)
# calculate how many characters are completed
$completed = [math]::Abs([math]::Round(($p / 100) * $midwidth, 0) - 1)
# generate dashes to symbolise completed
if ($completed -gt 1) {
$dashes = [string]::Join('', ((1..$completed) | ForEach-Object { '=' }))
}
# this is why we calculate $completed - 1 above
$dashes += switch ($p) {
100 { '=' }
default { '>' }
}
# the remaining characters are filled with spaces
$spaces = switch ($dashes.Length) {
$midwidth { [string]::Empty }
default {
[string]::Join('', ((1..($midwidth - $dashes.Length)) | ForEach-Object { ' ' }))
}
}
"$left [$dashes$spaces] $right"
}
function Write-DownloadProgress ($read, $total, $url) {
$console = $Host.UI.RawUI
$left = $console.CursorPosition.X
$top = $console.CursorPosition.Y
$width = $console.BufferSize.Width
if ($read -eq 0) {
$maxOutputLength = $(Format-DownloadProgress $url 100 $total $console).Length
if (($left + $maxOutputLength) -gt $width) {
# not enough room to print progress on this line
# print on new line
Write-Host
$left = 0
$top = $top + 1
if ($top -gt $console.CursorPosition.Y) { $top = $console.CursorPosition.Y }
}
}
Write-Host $(Format-DownloadProgress $url $read $total $console) -NoNewline
[console]::SetCursorPosition($left, $top)
}
## Aria2 downloader
function Test-Aria2Enabled {
return (Test-HelperInstalled -Helper Aria2) -and (get_config 'aria2-enabled' $true)
}
function aria_exit_code($exitcode) {
$codes = @{
0 = 'All downloads were successful'
1 = 'An unknown error occurred'
2 = 'Timeout'
3 = 'Resource was not found'
4 = 'Aria2 saw the specified number of "resource not found" error. See --max-file-not-found option'
5 = 'Download aborted because download speed was too slow. See --lowest-speed-limit option'
6 = 'Network problem occurred.'
7 = 'There were unfinished downloads. This error is only reported if all finished downloads were successful and there were unfinished downloads in a queue when aria2 exited by pressing Ctrl-C by an user or sending TERM or INT signal'
8 = 'Remote server did not support resume when resume was required to complete download'
9 = 'There was not enough disk space available'
10 = 'Piece length was different from one in .aria2 control file. See --allow-piece-length-change option'
11 = 'Aria2 was downloading same file at that moment'
12 = 'Aria2 was downloading same info hash torrent at that moment'
13 = 'File already existed. See --allow-overwrite option'
14 = 'Renaming file failed. See --auto-file-renaming option'
15 = 'Aria2 could not open existing file'
16 = 'Aria2 could not create new file or truncate existing file'
17 = 'File I/O error occurred'
18 = 'Aria2 could not create directory'
19 = 'Name resolution failed'
20 = 'Aria2 could not parse Metalink document'
21 = 'FTP command failed'
22 = 'HTTP response header was bad or unexpected'
23 = 'Too many redirects occurred'
24 = 'HTTP authorization failed'
25 = 'Aria2 could not parse bencoded file (usually ".torrent" file)'
26 = '".torrent" file was corrupted or missing information that aria2 needed'
27 = 'Magnet URI was bad'
28 = 'Bad/unrecognized option was given or unexpected option argument was given'
29 = 'The remote server was unable to handle the request due to a temporary overloading or maintenance'
30 = 'Aria2 could not parse JSON-RPC request'
31 = 'Reserved. Not used'
32 = 'Checksum validation failed'
}
if ($null -eq $codes[$exitcode]) {
return 'An unknown error occurred'
}
return $codes[$exitcode]
}
function get_filename_from_metalink($file) {
$bytes = get_magic_bytes_pretty $file ''
# check if file starts with '<?xml'
if (!($bytes.StartsWith('3c3f786d6c'))) {
return $null
}
# Add System.Xml for reading metalink files
Add-Type -AssemblyName 'System.Xml'
$xr = [System.Xml.XmlReader]::Create($file)
$filename = $null
try {
$xr.ReadStartElement('metalink')
if ($xr.ReadToFollowing('file') -and $xr.MoveToFirstAttribute()) {
$filename = $xr.Value
}
} catch [System.Xml.XmlException] {
return $null
} finally {
$xr.Close()
}
return $filename
}
function Invoke-CachedAria2Download ($app, $version, $manifest, $architecture, $dir, $cookies = $null, $use_cache = $true, $check_hash = $true) {
$data = @{}
$urls = @(script:url $manifest $architecture)
# aria2 input file
$urlstxt = Join-Path $cachedir "$app.txt"
$urlstxt_content = ''
$download_finished = $true
# aria2 options
$options = @(
"--input-file='$urlstxt'"
"--user-agent='$(Get-UserAgent)'"
'--allow-overwrite=true'
'--auto-file-renaming=false'
"--retry-wait=$(get_config 'aria2-retry-wait' 2)"
"--split=$(get_config 'aria2-split' 5)"
"--max-connection-per-server=$(get_config 'aria2-max-connection-per-server' 5)"
"--min-split-size=$(get_config 'aria2-min-split-size' '5M')"
'--console-log-level=warn'
'--enable-color=false'
'--no-conf=true'
'--follow-metalink=true'
'--metalink-preferred-protocol=https'
'--min-tls-version=TLSv1.2'
"--stop-with-process=$PID"
'--continue'
'--summary-interval=0'
'--auto-save-interval=1'
)
if ($cookies) {
$options += "--header='Cookie: $(cookie_header $cookies)'"
}
$proxy = get_config PROXY
if ($proxy -ne 'none') {
if ([Net.Webrequest]::DefaultWebProxy.Address) {
$options += "--all-proxy='$([Net.Webrequest]::DefaultWebProxy.Address.Authority)'"
}
if ([Net.Webrequest]::DefaultWebProxy.Credentials.UserName) {
$options += "--all-proxy-user='$([Net.Webrequest]::DefaultWebProxy.Credentials.UserName)'"
}
if ([Net.Webrequest]::DefaultWebProxy.Credentials.Password) {
$options += "--all-proxy-passwd='$([Net.Webrequest]::DefaultWebProxy.Credentials.Password)'"
}
}
$more_options = get_config 'aria2-options'
if ($more_options) {
$options += $more_options
}
foreach ($url in $urls) {
$data.$url = @{
'target' = Join-Path $dir (url_filename $url)
'cachename' = fname (cache_path $app $version $url)
'source' = cache_path $app $version $url
}
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) -ForegroundColor Cyan -NoNewline
Write-Host ' from cache.'
} else {
$download_finished = $false
# create aria2 input file content
try {
$try_url = handle_special_urls $url
} catch {
if ($_.Exception.Response.StatusCode -eq 'Unauthorized') {
warn 'Token might be misconfigured.'
}
}
$urlstxt_content += "$try_url`n"
if (!$url.Contains('sourceforge.net')) {
$urlstxt_content += " referer=$(strip_filename $url)`n"
}
$urlstxt_content += " dir=$cachedir`n"
$urlstxt_content += " out=$($data.$url.cachename)`n"
}
}
if (-not($download_finished)) {
# write aria2 input file
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 ...'
# Set console output encoding to UTF8 for non-ASCII characters printing
$oriConsoleEncoding = [Console]::OutputEncoding
[Console]::OutputEncoding = New-Object System.Text.UTF8Encoding
Invoke-Command ([scriptblock]::Create($aria2)) | ForEach-Object {
# Skip blank lines
if ([String]::IsNullOrWhiteSpace($_)) { return }
# Prevent potential overlaping of text when one line is shorter
$len = $Host.UI.RawUI.WindowSize.Width - $_.Length - 20
$blank = if ($len -gt 0) { ' ' * $len } else { '' }
$color = 'Gray'
if ($_.StartsWith('(OK):')) {
$noNewLine = $true
$color = 'Green'
} elseif ($_.StartsWith('[') -and $_.EndsWith(']')) {
$noNewLine = $true
$color = 'Cyan'
} elseif ($_.StartsWith('Download Results:')) {
$noNewLine = $false
}
Write-Host "`rDownload: $_$blank" -ForegroundColor $color -NoNewline:$noNewLine
}
Write-Host ''
if ($lastexitcode -gt 0) {
warn "Download failed! (Error $lastexitcode) $(aria_exit_code $lastexitcode)"
warn $urlstxt_content
warn $aria2
warn $(new_issue_msg $app $bucket "download via aria2 failed")
Write-Host "Fallback to default downloader ..."
try {
foreach ($url in $urls) {
Invoke-CachedDownload $app $version $url "$($data.$url.target)" $cookies $use_cache
}
} catch {
Write-Host $_ -ForegroundColor DarkRed
abort "URL $url is not valid"
}
}
# remove aria2 input file when done
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) {
$metalink_filename = get_filename_from_metalink $data.$url.source
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) {
$manifest_hash = hash_for_url $manifest $url $architecture
$ok, $err = check_hash $data.$url.source $manifest_hash $(show_app $app $bucket)
if (!$ok) {
error $err
if (Test-Path $data.$url.source) {
# rm cached file
Remove-Item $data.$url.source -Force -ErrorAction SilentlyContinue
Remove-Item "$($data.$url.source).aria2*" -Force -ErrorAction SilentlyContinue
}
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')
}
}
# copy or move file to target location
if (!(Test-Path $data.$url.source) ) {
abort $(new_issue_msg $app $bucket 'cached file not found')
}
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
}
}
}
}
## Helper functions
### Downloader parameters
function cookie_header($cookies) {
if (!$cookies) { return }
$vals = $cookies.psobject.properties | ForEach-Object {
"$($_.name)=$($_.value)"
}
[string]::join(';', $vals)
}
function Get-Encoding($wc) {
if ($null -ne $wc.ResponseHeaders -and $wc.ResponseHeaders['Content-Type'] -match 'charset=([^;]*)') {
return [System.Text.Encoding]::GetEncoding($Matches[1])
} else {
return [System.Text.Encoding]::GetEncoding('utf-8')
}
}
function Get-UserAgent() {
return "Scoop/1.0 (+http://scoop.sh/) PowerShell/$($PSVersionTable.PSVersion.Major).$($PSVersionTable.PSVersion.Minor) (Windows NT $([System.Environment]::OSVersion.Version.Major).$([System.Environment]::OSVersion.Version.Minor); $(if(${env:ProgramFiles(Arm)}){'ARM64; '}elseif($env:PROCESSOR_ARCHITECTURE -eq 'AMD64'){'Win64; x64; '})$(if($env:PROCESSOR_ARCHITEW6432 -in 'AMD64','ARM64'){'WOW64; '})$PSEdition)"
}
function setup_proxy() {
# note: '@' and ':' in password must be escaped, e.g. 'p@ssword' -> p\@ssword'
$proxy = get_config PROXY
if (!$proxy) {
return
}
try {
$credentials, $address = $proxy -split '(?<!\\)@'
if (!$address) {
$address, $credentials = $credentials, $null # no credentials supplied
}
if ($address -eq 'none') {
[net.webrequest]::defaultwebproxy = $null
} elseif ($address -ne 'default') {
[net.webrequest]::defaultwebproxy = New-Object net.webproxy "http://$address"
}
if ($credentials -eq 'currentuser') {
[net.webrequest]::defaultwebproxy.credentials = [net.credentialcache]::defaultcredentials
} elseif ($credentials) {
$username, $password = $credentials -split '(?<!\\):' | ForEach-Object { $_ -replace '\\([@:])', '$1' }
[net.webrequest]::defaultwebproxy.credentials = New-Object net.networkcredential($username, $password)
}
} catch {
warn "Failed to use proxy '$proxy': $($_.exception.message)"
}
}
function Get-GitHubToken {
return $env:SCOOP_GH_TOKEN, (get_config GH_TOKEN) | Where-Object -Property Length -Value 0 -GT | Select-Object -First 1
}
function github_ratelimit_reached {
$api_link = 'https://api.github.com/rate_limit'
$ret = (download_json $api_link).rate.remaining -eq 0
if ($ret) {
Write-Host "GitHub API rate limit reached.`r`nPlease try again later or configure your API token using 'scoop config gh_token <your token>'."
}
$ret
}
### URL handling
function handle_special_urls($url) {
# FossHub.com
if ($url -match '^(?:.*fosshub.com\/)(?<name>.*)(?:\/|\?dwl=)(?<filename>.*)$') {
$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})') {
$Body.Add('projectId', $Matches.pid)
$Body.Add('releaseId', $Matches.rid)
}
$url = Invoke-RestMethod -Method Post -Uri 'https://api.fosshub.com/download/' -ContentType 'application/json' -Body (ConvertTo-Json $Body -Compress)
if ($null -eq $url.error) {
$url = $url.data.url
}
}
# Sourceforge.net
if ($url -match '(?:downloads\.)?sourceforge.net\/projects?\/(?<project>[^\/]+)\/(?:files\/)?(?<file>.*?)(?:$|\/download|\?)') {
# 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
}
### Remote file information
function download_json($url) {
$githubtoken = Get-GitHubToken
$authheader = @{}
if ($githubtoken) {
$authheader = @{'Authorization' = "token $githubtoken" }
}
$ProgressPreference = 'SilentlyContinue'
$result = Invoke-WebRequest $url -UseBasicParsing -Headers $authheader | Select-Object -ExpandProperty content | ConvertFrom-Json
$ProgressPreference = 'Continue'
$result
}
function get_magic_bytes($file) {
if (!(Test-Path $file)) {
return ''
}
if ((Get-Command Get-Content).parameters.ContainsKey('AsByteStream')) {
# PowerShell Core (6.0+) '-Encoding byte' is replaced by '-AsByteStream'
return Get-Content $file -AsByteStream -TotalCount 8
} else {
return Get-Content $file -Encoding byte -TotalCount 8
}
}
function get_magic_bytes_pretty($file, $glue = ' ') {
if (!(Test-Path $file)) {
return ''
}
return (get_magic_bytes $file | ForEach-Object { $_.ToString('x2') }) -join $glue
}
Function Get-RemoteFileSize ($Uri) {
$response = Invoke-WebRequest -Uri $Uri -Method HEAD -UseBasicParsing
if (!$response.Headers.StatusCode) {
$response.Headers.'Content-Length' | ForEach-Object { [int]$_ }
}
}
function ftp_file_size($url) {
$request = [net.ftpwebrequest]::create($url)
$request.method = [net.webrequestmethods+ftp]::getfilesize
$request.getresponse().contentlength
}
function url_filename($url) {
(Split-Path $url -Leaf).split('?') | Select-Object -First 1
}
function url_remote_filename($url) {
# Unlike url_filename which can be tricked by appending a
# URL fragment (e.g. #/dl.7z, useful for coercing a local filename),
# this function extracts the original filename from the URL.
$uri = (New-Object URI $url)
$basename = Split-Path $uri.PathAndQuery -Leaf
If ($basename -match '.*[?=]+([\w._-]+)') {
$basename = $matches[1]
}
If (($basename -notlike '*.*') -or ($basename -match '^[v.\d]+$')) {
$basename = Split-Path $uri.AbsolutePath -Leaf
}
If (($basename -notlike '*.*') -and ($uri.Fragment -ne '')) {
$basename = $uri.Fragment.Trim('/', '#')
}
return $basename
}
### Hash-related functions
function hash_for_url($manifest, $url, $arch) {
$hashes = @(hash $manifest $arch) | Where-Object { $_ -ne $null }
if ($hashes.length -eq 0) { return $null }
$urls = @(script:url $manifest $arch)
$index = [array]::IndexOf($urls, $url)
if ($index -eq -1) { abort "Couldn't find hash in manifest for '$url'." }
@($hashes)[$index]
}
function check_hash($file, $hash, $app_name) {
# returns (ok, err)
if (!$hash) {
warn "Warning: No hash in manifest. SHA256 for '$(fname $file)' is:`n $((Get-FileHash -Path $file -Algorithm SHA256).Hash.ToLower())"
return $true, $null
}
Write-Host 'Checking hash of ' -NoNewline
Write-Host $(url_remote_filename $url) -ForegroundColor Cyan -NoNewline
Write-Host ' ... ' -NoNewline
$algorithm, $expected = get_hash $hash
if ($null -eq $algorithm) {
return $false, "Hash type '$algorithm' isn't supported."
}
$actual = (Get-FileHash -Path $file -Algorithm $algorithm).Hash.ToLower()
$expected = $expected.ToLower()
if ($actual -ne $expected) {
$msg = "Hash check failed!`n"
$msg += "App: $app_name`n"
$msg += "URL: $url`n"
if (Test-Path $file) {
$msg += "First bytes: $((get_magic_bytes_pretty $file ' ').ToUpper())`n"
}
if ($expected -or $actual) {
$msg += "Expected: $expected`n"
$msg += "Actual: $actual"
}
return $false, $msg
}
Write-Host 'ok.' -f Green
return $true, $null
}
function get_hash([String] $multihash) {
$type, $hash = $multihash -split ':'
if (!$hash) {
# no type specified, assume sha256
$type, $hash = 'sha256', $multihash
}
if (@('md5', 'sha1', 'sha256', 'sha512') -notcontains $type) {
return $null, "Hash type '$type' isn't supported."
}
return $type, $hash.ToLower()
}
# Setup proxy globally
setup_proxy

View File

@@ -13,7 +13,7 @@
# following arguments are treated as non-option arguments, even if
# they begin with a hyphen. The "--" itself will not be included in
# the returned $opts. (POSIX-compatible)
function getopt($argv, $shortopts, $longopts) {
function getopt([String[]]$argv, [String]$shortopts, [String[]]$longopts) {
$opts = @{}; $rem = @()
function err($msg) {
@@ -24,10 +24,6 @@ function getopt($argv, $shortopts, $longopts) {
return [Regex]::Escape($str)
}
# ensure these are arrays
$argv = @($argv -split ' ')
$longopts = @($longopts)
for ($i = 0; $i -lt $argv.Length; $i++) {
$arg = $argv[$i]
if ($null -eq $arg) { continue }
@@ -81,6 +77,5 @@ function getopt($argv, $shortopts, $longopts) {
$rem += $arg
}
}
$opts, $rem
}

File diff suppressed because it is too large Load Diff

View File

@@ -110,13 +110,13 @@ function json_path([String] $json, [String] $jsonpath, [Hashtable] $substitution
# Return versions in reverse order
$result = [System.Linq.Enumerable]::Reverse($result)
}
if ($single) {
if ([System.Linq.Enumerable]::Count($result) -eq 1 -or $single) {
# Extract First value
$result = [System.Linq.Enumerable]::First($result)
# Convert first value to string
$result = $result.ToString()
} else {
$result = "$([String]::Join('\n', $result))"
$result = [Newtonsoft.Json.JsonConvert]::SerializeObject($result)
}
return $result
} catch [Exception] {

View File

@@ -7,7 +7,7 @@ function parse_json($path) {
try {
Get-Content $path -Raw -Encoding UTF8 | ConvertFrom-Json -ErrorAction Stop
} catch {
warn "Error parsing JSON at $path."
warn "Error parsing JSON at '$path'."
}
}
@@ -23,11 +23,11 @@ function url_manifest($url) {
} catch {
throw
}
if(!$str) { return $null }
if (!$str) { return $null }
try {
$str | ConvertFrom-Json -ErrorAction Stop
} catch {
warn "Error parsing JSON at $url."
warn "Error parsing JSON at '$url'."
}
}
@@ -40,31 +40,74 @@ function Get-Manifest($app) {
$app = appname_from_url $url
$manifest = url_manifest $url
} else {
$app, $bucket, $version = parse_app $app
if ($bucket) {
$manifest = manifest $app $bucket
# Check if the manifest is already installed
if (installed $app) {
$global = installed $app $true
$ver = Select-CurrentVersion -AppName $app -Global:$global
if (!$ver) {
$app, $bucket, $ver = parse_app $app
$ver = Select-CurrentVersion -AppName $app -Global:$global
}
$install_info_path = "$(versiondir $app $ver $global)\install.json"
if (Test-Path $install_info_path) {
$install_info = parse_json $install_info_path
$bucket = $install_info.bucket
if (!$bucket) {
$url = $install_info.url
if ($url -match '^(ht|f)tps?://|\\\\') {
$manifest = url_manifest $url
}
if (!$manifest) {
if (Test-Path $url) {
$manifest = parse_json $url
} else {
# Fallback to installed manifest
$manifest = installed_manifest $app $ver $global
}
}
} else {
$manifest = manifest $app $bucket
if (!$manifest) {
$deprecated_dir = (Find-BucketDirectory -Name $bucket -Root) + '\deprecated'
$manifest = parse_json (Get-ChildItem $deprecated_dir -Filter "$(sanitary_path $app).json" -Recurse -ErrorAction Ignore).FullName
}
}
}
} else {
foreach ($bucket in Get-LocalBucket) {
$app, $bucket, $version = parse_app $app
if ($bucket) {
$manifest = manifest $app $bucket
if ($manifest) {
break
} else {
$matched_buckets = @()
foreach ($tekcub in Get-LocalBucket) {
$current_manifest = manifest $app $tekcub
if (!$manifest -and $current_manifest) {
$manifest = $current_manifest
$bucket = $tekcub
}
if ($current_manifest) {
$matched_buckets += $tekcub
}
}
}
if (!$manifest) {
# couldn't find app in buckets: check if it's a local path
if (Test-Path $app) {
$url = Convert-Path $app
$app = appname_from_url $url
$manifest = parse_json $url
} else {
if (($app -match '\\/') -or $app.EndsWith('.json')) { $url = $app }
$app = appname_from_url $app
}
}
}
if (!$manifest) {
# couldn't find app in buckets: check if it's a local path
$appPath = $app
$bucket = $null
if (!$appPath.EndsWith('.json')) {
$appPath += '.json'
}
if (Test-Path $appPath) {
$url = Convert-Path $appPath
$app = appname_from_url $url
$manifest = url_manifest $url
}
}
}
if ($matched_buckets.Length -gt 1) {
warn "Multiple buckets contain manifest '$app', the current selection is '$bucket/$app'."
}
return $app, $manifest, $bucket, $url
}
@@ -138,16 +181,26 @@ function generate_user_manifest($app, $bucket, $version) {
warn "Given version ($version) does not match manifest ($($manifest.version))"
warn "Attempting to generate manifest for '$app' ($version)"
ensure (usermanifestsdir) | Out-Null
$manifest_path = "$(usermanifestsdir)\$app.json"
if (get_config USE_SQLITE_CACHE) {
$cached_manifest = (Get-ScoopDBItem -Name $app -Bucket $bucket -Version $version).manifest
if ($cached_manifest) {
$cached_manifest | Out-UTF8File $manifest_path
return $manifest_path
}
}
if (!($manifest.autoupdate)) {
abort "'$app' does not have autoupdate capability`r`ncouldn't find manifest for '$app@$version'"
}
ensure (usermanifestsdir) | out-null
try {
Invoke-AutoUpdate $app "$(Convert-Path (usermanifestsdir))\$app.json" $manifest $version $(@{ })
return Convert-Path (usermanifest $app)
Invoke-AutoUpdate $app $manifest_path $manifest $version $(@{ })
return $manifest_path
} catch {
write-host -f darkred "Could not install $app@$version"
Write-Host -ForegroundColor DarkRed "Could not install $app@$version"
}
return $null
@@ -156,7 +209,6 @@ function generate_user_manifest($app, $bucket, $version) {
function url($manifest, $arch) { arch_specific 'url' $manifest $arch }
function installer($manifest, $arch) { arch_specific 'installer' $manifest $arch }
function uninstaller($manifest, $arch) { arch_specific 'uninstaller' $manifest $arch }
function msi($manifest, $arch) { arch_specific 'msi' $manifest $arch }
function hash($manifest, $arch) { arch_specific 'hash' $manifest $arch }
function extract_dir($manifest, $arch) { arch_specific 'extract_dir' $manifest $arch}
function extract_to($manifest, $arch) { arch_specific 'extract_to' $manifest $arch}
function extract_dir($manifest, $arch) { arch_specific 'extract_dir' $manifest $arch }
function extract_to($manifest, $arch) { arch_specific 'extract_to' $manifest $arch }

View File

@@ -1,22 +1,17 @@
$modulesdir = "$scoopdir\modules"
function install_psmodule($manifest, $dir, $global) {
$psmodule = $manifest.psmodule
if (!$psmodule) { return }
if ($global) {
abort 'Installing PowerShell modules globally is not implemented!'
}
$targetdir = ensure (modulesdir $global)
$modulesdir = ensure $modulesdir
ensure_in_psmodulepath $modulesdir $global
ensure_in_psmodulepath $targetdir $global
$module_name = $psmodule.name
if (!$module_name) {
abort "Invalid manifest: The 'name' property is missing from 'psmodule'."
}
$linkfrom = "$modulesdir\$module_name"
$linkfrom = "$targetdir\$module_name"
Write-Host "Installing PowerShell module '$module_name'"
Write-Host "Linking $(friendly_path $linkfrom) => $(friendly_path $dir)"
@@ -36,7 +31,9 @@ function uninstall_psmodule($manifest, $dir, $global) {
$module_name = $psmodule.name
Write-Host "Uninstalling PowerShell module '$module_name'."
$linkfrom = "$modulesdir\$module_name"
$targetdir = modulesdir $global
$linkfrom = "$targetdir\$module_name"
if (Test-Path $linkfrom) {
Write-Host "Removing $(friendly_path $linkfrom)"
$linkfrom = Convert-Path $linkfrom
@@ -45,15 +42,13 @@ function uninstall_psmodule($manifest, $dir, $global) {
}
function ensure_in_psmodulepath($dir, $global) {
$path = env 'psmodulepath' $global
$path = Get-EnvVar -Name 'PSModulePath' -Global:$global
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."
env 'psmodulepath' $global "$dir;$path" # for future sessions...
$env:psmodulepath = "$dir;$env:psmodulepath" # for this session
Set-EnvVar -Name 'PSModulePath' -Value "$dir;$path" -Global:$global
}
}

View File

@@ -5,16 +5,16 @@ function create_startmenu_shortcuts($manifest, $dir, $global, $arch) {
$target = [System.IO.Path]::Combine($dir, $_.item(0))
$target = New-Object System.IO.FileInfo($target)
$name = $_.item(1)
$arguments = ""
$arguments = ''
$icon = $null
if($_.length -ge 3) {
if ($_.length -ge 3) {
$arguments = $_.item(2)
}
if($_.length -ge 4) {
if ($_.length -ge 4) {
$icon = [System.IO.Path]::Combine($dir, $_.item(3))
$icon = New-Object System.IO.FileInfo($icon)
}
$arguments = (substitute $arguments @{ '$dir' = $dir; '$original_dir' = $original_dir; '$persist_dir' = $persist_dir})
$arguments = (substitute $arguments @{ '$dir' = $dir; '$original_dir' = $original_dir; '$persist_dir' = $persist_dir })
startmenu_shortcut $target $name $arguments $icon $global
}
}
@@ -29,11 +29,11 @@ function shortcut_folder($global) {
}
function startmenu_shortcut([System.IO.FileInfo] $target, $shortcutName, $arguments, [System.IO.FileInfo]$icon, $global) {
if(!$target.Exists) {
if (!$target.Exists) {
Write-Host -f DarkRed "Creating shortcut for $shortcutName ($(fname $target)) failed: Couldn't find $target"
return
}
if($icon -and !$icon.Exists) {
if ($icon -and !$icon.Exists) {
Write-Host -f DarkRed "Creating shortcut for $shortcutName ($(fname $target)) failed: Couldn't find icon $icon"
return
}
@@ -51,11 +51,11 @@ function startmenu_shortcut([System.IO.FileInfo] $target, $shortcutName, $argume
if ($arguments) {
$wsShell.Arguments = $arguments
}
if($icon -and $icon.Exists) {
if ($icon -and $icon.Exists) {
$wsShell.IconLocation = $icon.FullName
}
$wsShell.Save()
write-host "Creating shortcut for $shortcutName ($(fname $target))"
Write-Host "Creating shortcut for $shortcutName ($(fname $target))"
}
# Removes the Startmenu shortcut if it exists
@@ -63,10 +63,10 @@ function rm_startmenu_shortcuts($manifest, $global, $arch) {
$shortcuts = @(arch_specific 'shortcuts' $manifest $arch)
$shortcuts | Where-Object { $_ -ne $null } | ForEach-Object {
$name = $_.item(1)
$shortcut = "$(shortcut_folder $global)\$name.lnk"
write-host "Removing shortcut $(friendly_path $shortcut)"
if(Test-Path -Path $shortcut) {
Remove-Item $shortcut
$shortcut = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath("$(shortcut_folder $global)\$name.lnk")
Write-Host "Removing shortcut $(friendly_path $shortcut)"
if (Test-Path -Path $shortcut) {
Remove-Item $shortcut
}
}
}

176
lib/system.ps1 Normal file
View File

@@ -0,0 +1,176 @@
# System-related functions
## Environment Variables
function Publish-EnvVar {
if (-not ('Win32.NativeMethods' -as [Type])) {
Add-Type -Namespace Win32 -Name NativeMethods -MemberDefinition @'
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr SendMessageTimeout(
IntPtr hWnd, uint Msg, UIntPtr wParam, string lParam,
uint fuFlags, uint uTimeout, out UIntPtr lpdwResult
);
'@
}
$HWND_BROADCAST = [IntPtr] 0xffff
$WM_SETTINGCHANGE = 0x1a
$result = [UIntPtr]::Zero
[Win32.NativeMethods]::SendMessageTimeout($HWND_BROADCAST,
$WM_SETTINGCHANGE,
[UIntPtr]::Zero,
'Environment',
2,
5000,
[ref] $result
) | Out-Null
}
function Get-EnvVar {
param(
[string]$Name,
[switch]$Global
)
$registerKey = if ($Global) {
Get-Item -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager'
} else {
Get-Item -Path 'HKCU:'
}
$envRegisterKey = $registerKey.OpenSubKey('Environment')
$registryValueOption = [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames
$envRegisterKey.GetValue($Name, $null, $registryValueOption)
}
function Set-EnvVar {
param(
[string]$Name,
[string]$Value,
[switch]$Global
)
$registerKey = if ($Global) {
Get-Item -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager'
} else {
Get-Item -Path 'HKCU:'
}
$envRegisterKey = $registerKey.OpenSubKey('Environment', $true)
if ($null -eq $Value -or $Value -eq '') {
if ($envRegisterKey.GetValue($Name)) {
$envRegisterKey.DeleteValue($Name)
}
} else {
$registryValueKind = if ($Value.Contains('%')) {
[Microsoft.Win32.RegistryValueKind]::ExpandString
} elseif ($envRegisterKey.GetValue($Name)) {
$envRegisterKey.GetValueKind($Name)
} else {
[Microsoft.Win32.RegistryValueKind]::String
}
$envRegisterKey.SetValue($Name, $Value, $registryValueKind)
}
Publish-EnvVar
}
function Split-PathLikeEnvVar {
param(
[string[]]$Pattern,
[string]$Path
)
if ($null -eq $Path -and $Path -eq '') {
return $null, $null
} else {
$splitPattern = $Pattern.Split(';', [System.StringSplitOptions]::RemoveEmptyEntries)
$splitPath = $Path.Split(';', [System.StringSplitOptions]::RemoveEmptyEntries)
$inPath = @()
foreach ($p in $splitPattern) {
$inPath += $splitPath.Where({ $_ -like $p })
$splitPath = $splitPath.Where({ $_ -notlike $p })
}
return ($inPath -join ';'), ($splitPath -join ';')
}
}
function Add-Path {
param(
[string[]]$Path,
[string]$TargetEnvVar = 'PATH',
[switch]$Global,
[switch]$Force,
[switch]$Quiet
)
# future sessions
$inPath, $strippedPath = Split-PathLikeEnvVar $Path (Get-EnvVar -Name $TargetEnvVar -Global:$Global)
if (!$inPath -or $Force) {
if (!$Quiet) {
$Path | ForEach-Object {
Write-Host "Adding $(friendly_path $_) to $(if ($Global) {'global'} else {'your'}) path."
}
}
Set-EnvVar -Name $TargetEnvVar -Value ((@($Path) + $strippedPath) -join ';') -Global:$Global
}
# current session
$inPath, $strippedPath = Split-PathLikeEnvVar $Path $env:PATH
if (!$inPath -or $Force) {
$env:PATH = (@($Path) + $strippedPath) -join ';'
}
}
function Remove-Path {
param(
[string[]]$Path,
[string]$TargetEnvVar = 'PATH',
[switch]$Global,
[switch]$Quiet,
[switch]$PassThru
)
# future sessions
$inPath, $strippedPath = Split-PathLikeEnvVar $Path (Get-EnvVar -Name $TargetEnvVar -Global:$Global)
if ($inPath) {
if (!$Quiet) {
$Path | ForEach-Object {
Write-Host "Removing $(friendly_path $_) from $(if ($Global) {'global'} else {'your'}) path."
}
}
Set-EnvVar -Name $TargetEnvVar -Value $strippedPath -Global:$Global
}
# current session
$inSessionPath, $strippedPath = Split-PathLikeEnvVar $Path $env:PATH
if ($inSessionPath) {
$env:PATH = $strippedPath
}
if ($PassThru) {
return $inPath
}
}
## Deprecated functions
function env($name, $global, $val) {
if ($PSBoundParameters.ContainsKey('val')) {
Show-DeprecatedWarning $MyInvocation 'Set-EnvVar'
Set-EnvVar -Name $name -Value $val -Global:$global
} else {
Show-DeprecatedWarning $MyInvocation 'Get-EnvVar'
Get-EnvVar -Name $name -Global:$global
}
}
function strip_path($orig_path, $dir) {
Show-DeprecatedWarning $MyInvocation 'Split-PathLikeEnvVar'
Split-PathLikeEnvVar -Pattern @($dir) -Path $orig_path
}
function add_first_in_path($dir, $global) {
Show-DeprecatedWarning $MyInvocation 'Add-Path'
Add-Path -Path $dir -Global:$global -Force
}
function remove_from_path($dir, $global) {
Show-DeprecatedWarning $MyInvocation 'Remove-Path'
Remove-Path -Path $dir -Global:$global
}

View File

@@ -1,114 +1,68 @@
# Usage: scoop alias add|list|rm [<args>]
# Usage: scoop alias <subcommand> [options] [<args>]
# Summary: Manage scoop aliases
# Help: Add, remove or list Scoop aliases
# Help: Available subcommands: add, rm, list.
#
# Aliases are custom Scoop subcommands that can be created to make common tasks
# easier.
# Aliases are custom Scoop subcommands that can be created to make common tasks easier.
#
# To add an Alias:
# scoop alias add <name> <command> <description>
# To add an alias:
#
# e.g.:
# scoop alias add rm 'scoop uninstall $args[0]' 'Uninstalls an app'
# scoop alias add upgrade 'scoop update *' 'Updates all apps, just like brew or apt'
# scoop alias add <name> <command> [<description>]
#
# e.g.,
#
# scoop alias add rm 'scoop uninstall $args[0]' 'Uninstall an app'
# scoop alias add upgrade 'scoop update *' 'Update all apps, just like "brew" or "apt"'
#
# To remove an alias:
#
# scoop alias rm <name>
#
# To list all aliases:
#
# scoop alias list [-v|--verbose]
#
# Options:
# -v, --verbose Show alias description and table headers (works only for 'list')
# -v, --verbose Show alias description and table headers (works only for "list")
param(
[String]$opt,
[String]$name,
[String]$command,
[String]$description,
[Switch]$verbose = $false
)
param($SubCommand)
. "$PSScriptRoot\..\lib\install.ps1" # shim related
. "$PSScriptRoot\..\lib\getopt.ps1"
$script:config_alias = 'alias'
function init_alias_config {
$aliases = get_config $script:config_alias
if ($aliases) {
$aliases
$SubCommands = @('add', 'rm', 'list')
if ($SubCommand -notin $SubCommands) {
if (!$SubCommand) {
error '<subcommand> missing'
} else {
New-Object -TypeName PSObject
error "'$SubCommand' is not one of available subcommands: $($SubCommands -join ', ')"
}
my_usage
exit 1
}
function add_alias($name, $command) {
if (!$command) {
abort "Can't create an empty alias."
$opt, $other, $err = getopt $Args 'v' 'verbose'
if ($err) { "scoop alias: $err"; exit 1 }
$name, $command, $description = $other
$verbose = $opt.v -or $opt.verbose
switch ($SubCommand) {
'add' {
if (!$name -or !$command) {
error "<name> and <command> must be specified for subcommand 'add'"
exit 1
}
add_alias $name $command $description
}
# get current aliases from config
$aliases = init_alias_config
if ($aliases.$name) {
abort "Alias $name already exists."
'rm' {
if (!$name) {
error "<name> must be specified for subcommand 'rm'"
exit 1
}
rm_alias $name
}
$alias_file = "scoop-$name"
# generate script
$shimdir = shimdir $false
$script =
@(
"# Summary: $description",
"$command"
) -join "`r`n"
$script | Out-UTF8File "$shimdir\$alias_file.ps1"
# add alias to config
$aliases | Add-Member -MemberType NoteProperty -Name $name -Value $alias_file
set_config $script:config_alias $aliases | Out-Null
}
function rm_alias($name) {
$aliases = init_alias_config
if (!$name) {
abort 'Which alias should be removed?'
'list' {
list_aliases $verbose
}
if ($aliases.$name) {
"Removing alias $name..."
rm_shim $aliases.$name (shimdir $false)
$aliases.PSObject.Properties.Remove($name)
set_config $script:config_alias $aliases | Out-Null
} else {
abort "Alias $name doesn't exist."
}
}
function list_aliases {
$aliases = @()
(init_alias_config).PSObject.Properties.GetEnumerator() | ForEach-Object {
$content = Get-Content (command_path $_.Name)
$command = ($content | Select-Object -Skip 1).Trim()
$summary = (summary $content).Trim()
$aliases += New-Object psobject -Property @{Name = $_.name; Summary = $summary; Command = $command }
}
if (!$aliases.count) {
info "No alias found."
}
$aliases = $aliases.GetEnumerator() | Sort-Object Name
if ($verbose) {
return $aliases | Select-Object Name, Command, Summary
} else {
return $aliases | Select-Object Name, Command
}
}
switch ($opt) {
'add' { add_alias $name $command }
'rm' { rm_alias $name }
'list' { list_aliases }
default { my_usage; exit 1 }
}
exit 0

View File

@@ -19,6 +19,15 @@
# scoop bucket known
param($cmd, $name, $repo)
if (get_config NO_JUNCTION) {
. "$PSScriptRoot\..\lib\versions.ps1"
}
if (get_config USE_SQLITE_CACHE) {
. "$PSScriptRoot\..\lib\manifest.ps1"
. "$PSScriptRoot\..\lib\database.ps1"
}
$usage_add = 'usage: scoop bucket add <name> [<repo>]'
$usage_rm = 'usage: scoop bucket rm <name>'

View File

@@ -16,7 +16,7 @@ param($cmd)
function cacheinfo($file) {
$app, $version, $url = $file.Name -split '#'
New-Object PSObject -Property @{ Name = $app; Version = $version; Length = $file.Length; URL = $url }
New-Object PSObject -Property @{ Name = $app; Version = $version; Length = $file.Length }
}
function cacheshow($app) {
@@ -28,7 +28,7 @@ function cacheshow($app) {
$files = @(Get-ChildItem $cachedir | Where-Object -Property Name -Value "^$app#" -Match)
$totalLength = ($files | Measure-Object -Property Length -Sum).Sum
$files | ForEach-Object { cacheinfo $_ } | Select-Object Name, Version, Length, URL
$files | ForEach-Object { cacheinfo $_ } | Select-Object Name, Version, Length
Write-Host "Total: $($files.Length) $(pluralize $files.Length 'file' 'files'), $(filesize $totalLength)" -ForegroundColor Yellow
}
@@ -48,7 +48,7 @@ function cacheremove($app) {
$files | ForEach-Object {
$curr = cacheinfo $_
Write-Host "Removing $($curr.URL)..."
Write-Host "Removing $($_.Name)..."
Remove-Item $_.FullName
if(Test-Path "$cachedir\$($curr.Name).txt") {
Remove-Item "$cachedir\$($curr.Name).txt"

View File

@@ -7,21 +7,23 @@
param($app)
. "$PSScriptRoot\..\lib\json.ps1" # 'ConvertToPrettyJson'
. "$PSScriptRoot\..\lib\versions.ps1" # 'Select-CurrentVersion'
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Get-Manifest'
. "$PSScriptRoot\..\lib\download.ps1" # 'Get-UserAgent'
if (!$app) { error '<app> missing'; my_usage; exit 1 }
$null, $manifest, $bucket, $url = Get-Manifest $app
if ($manifest) {
$style = get_config CAT_STYLE
if ($style) {
$manifest | ConvertToPrettyJson | bat --no-paging --style $style --language json
} else {
$manifest | ConvertToPrettyJson
}
$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" })."
abort "Couldn't find manifest for '$app'$(if($bucket) { " from '$bucket' bucket" } elseif($url) { " at '$url'" })."
}
exit $exitCode

View File

@@ -10,7 +10,7 @@ $defenderIssues = 0
$adminPrivileges = ([System.Security.Principal.WindowsPrincipal] [System.Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)
if ($adminPrivileges) {
if ($adminPrivileges -and $env:USERNAME -ne 'WDAGUtilityAccount') {
$defenderIssues += !(check_windows_defender $false)
$defenderIssues += !(check_windows_defender $true)
}
@@ -19,18 +19,18 @@ $issues += !(check_main_bucket)
$issues += !(check_long_paths)
$issues += !(Get-WindowsDeveloperModeStatus)
if (!(Test-HelperInstalled -Helper 7zip)) {
error "'7-Zip' is not installed! It's required for unpacking most programs. Please Run 'scoop install 7zip' or 'scoop install 7zip-zstd'."
if (!(Test-HelperInstalled -Helper 7zip) -and !(get_config USE_EXTERNAL_7ZIP)) {
warn "'7-Zip' is not installed! It's required for unpacking most programs. Please Run 'scoop install 7zip'."
$issues++
}
if (!(Test-HelperInstalled -Helper Innounp)) {
error "'Inno Setup Unpacker' is not installed! It's required for unpacking InnoSetup files. Please run 'scoop install innounp'."
warn "'Inno Setup Unpacker' is not installed! It's required for unpacking InnoSetup files. Please run 'scoop install innounp'."
$issues++
}
if (!(Test-HelperInstalled -Helper Dark)) {
error "'dark' is not installed! It's required for unpacking installers created with the WiX Toolset. Please run 'scoop install dark' or 'scoop install wixtoolset'."
warn "'dark' is not installed! It's required for unpacking installers created with the WiX Toolset. Please run 'scoop install dark' or 'scoop install wixtoolset'."
$issues++
}

View File

@@ -27,6 +27,9 @@
# use_lessmsi: $true|$false
# Prefer lessmsi utility over native msiexec.
#
# use_sqlite_cache: $true|$false
# Use SQLite database for caching. This is useful for speeding up 'scoop search' and 'scoop shim' commands.
#
# no_junction: $true|$false
# The 'current' version alias will not be used. Shims and shortcuts will point to specific version instead.
#
@@ -115,6 +118,11 @@
# Nightly version is formatted as 'nightly-yyyyMMdd' and will be updated after one day if this is set to $true.
# Otherwise, nightly version will not be updated unless `--force` is used.
#
# use_isolated_path: $true|$false|[string]
# When set to $true, Scoop will use `SCOOP_PATH` environment variable to store apps' `PATH`s.
# When set to arbitrary non-empty string, Scoop will use that string as the environment variable name instead.
# This is useful when you want to isolate Scoop from the system `PATH`.
#
# ARIA2 configuration
# -------------------
#
@@ -151,30 +159,12 @@ if (!$name) {
} elseif ($name -like '--help') {
my_usage
} elseif ($name -like 'rm') {
# NOTE Scoop config file migration. Remove this after 2023/6/30
if ($value -notin 'SCOOP_REPO', 'SCOOP_BRANCH' -and $value -in $newConfigNames.Keys) {
warn ('Config option "{0}" is deprecated, please use "{1}" instead next time.' -f $value, $newConfigNames.$value)
$value = $newConfigNames.$value
}
# END NOTE
set_config $value $null | Out-Null
Write-Host "'$value' has been removed"
} elseif ($null -ne $value) {
# NOTE Scoop config file migration. Remove this after 2023/6/30
if ($name -notin 'SCOOP_REPO', 'SCOOP_BRANCH' -and $name -in $newConfigNames.Keys) {
warn ('Config option "{0}" is deprecated, please use "{1}" instead next time.' -f $name, $newConfigNames.$name)
$name = $newConfigNames.$name
}
# END NOTE
set_config $name $value | Out-Null
Write-Host "'$name' has been set to '$value'"
} else {
# NOTE Scoop config file migration. Remove this after 2023/6/30
if ($name -notin 'SCOOP_REPO', 'SCOOP_BRANCH' -and $name -in $newConfigNames.Keys) {
warn ('Config option "{0}" is deprecated, please use "{1}" instead next time.' -f $name, $newConfigNames.$name)
$name = $newConfigNames.$name
}
# END NOTE
$value = get_config $name
if($null -eq $value) {
Write-Host "'$name' is not set"

View File

@@ -59,7 +59,7 @@ function choose_item($list, $query) {
}
if (!$url) {
scoop help create
& "$PSScriptRoot\scoop-help.ps1" create
} else {
create_manifest $url
}

View File

@@ -3,7 +3,9 @@
. "$PSScriptRoot\..\lib\getopt.ps1"
. "$PSScriptRoot\..\lib\depends.ps1" # 'Get-Dependency'
. "$PSScriptRoot\..\lib\versions.ps1" # 'Select-CurrentVersion'
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Get-Manifest' (indirectly)
. "$PSScriptRoot\..\lib\download.ps1" # 'Get-UserAgent'
$opt, $apps, $err = getopt $args 'a:' 'arch='
$app = $apps[0]
@@ -20,7 +22,14 @@ try {
$deps = @()
Get-Dependency $app $architecture | ForEach-Object {
$dep = [ordered]@{}
$dep.Source, $dep.Name = $_ -split '/'
$app, $null, $bucket, $url = Get-Manifest $_
if (!$url) {
$bucket, $app = $_ -split '/'
}
$dep.Source = if ($url) { $url } else { $bucket }
$dep.Name = $app
$deps += [PSCustomObject]$dep
}
$deps

View File

@@ -15,20 +15,25 @@
#
# Options:
# -f, --force Force download (overwrite cache)
# -h, --no-hash-check Skip hash verification (use with caution!)
# -s, --skip-hash-check Skip hash verification (use with caution!)
# -u, --no-update-scoop Don't update Scoop before downloading if it's outdated
# -a, --arch <32bit|64bit|arm64> Use the specified architecture, if the app supports it
. "$PSScriptRoot\..\lib\getopt.ps1"
. "$PSScriptRoot\..\lib\json.ps1" # 'autoupdate.ps1' (indirectly)
. "$PSScriptRoot\..\lib\autoupdate.ps1" # 'generate_user_manifest' (indirectly)
. "$PSScriptRoot\..\lib\versions.ps1" # 'Select-CurrentVersion'
. "$PSScriptRoot\..\lib\manifest.ps1" # 'generate_user_manifest' 'Get-Manifest'
. "$PSScriptRoot\..\lib\install.ps1"
. "$PSScriptRoot\..\lib\download.ps1"
. "$PSScriptRoot\..\lib\install.ps1" # 'nightly_version'
if (get_config USE_SQLITE_CACHE) {
. "$PSScriptRoot\..\lib\database.ps1"
}
$opt, $apps, $err = getopt $args 'fhua:' 'force', 'no-hash-check', 'no-update-scoop', 'arch='
$opt, $apps, $err = getopt $args 'fsua:' 'force', 'skip-hash-check', 'no-update-scoop', 'arch='
if ($err) { error "scoop download: $err"; exit 1 }
$check_hash = !($opt.h -or $opt.'no-hash-check')
$check_hash = !($opt.s -or $opt.'skip-hash-check')
$use_cache = !($opt.f -or $opt.force)
$architecture = Get-DefaultArchitecture
try {
@@ -43,7 +48,7 @@ if (is_scoop_outdated) {
if ($opt.u -or $opt.'no-update-scoop') {
warn "Scoop is out of date."
} else {
scoop update
& "$PSScriptRoot\scoop-update.ps1"
}
}
@@ -70,7 +75,7 @@ foreach ($curr_app in $apps) {
}
if(!$manifest) {
error "Couldn't find manifest for '$app'$(if($url) { " at the URL $url" })."
error "Couldn't find manifest for '$app'$(if($bucket) { " from '$bucket' bucket" } elseif($url) { " at '$url'" })."
continue
}
$version = $manifest.version

View File

@@ -29,14 +29,13 @@ if ($global -and !(is_admin)) {
exit 1
}
$apps | ForEach-Object {
$app = $_
foreach ($app in $apps) {
if ($app -eq 'scoop') {
$hold_update_until = [System.DateTime]::Now.AddDays(1)
set_config HOLD_UPDATE_UNTIL $hold_update_until.ToString('o') | Out-Null
success "$app is now held and might not be updated until $($hold_update_until.ToLocalTime())."
return
continue
}
if (!(installed $app $global)) {
if ($global) {
@@ -44,18 +43,26 @@ $apps | ForEach-Object {
} else {
error "'$app' is not installed."
}
return
continue
}
if (get_config NO_JUNCTION){
if (get_config NO_JUNCTION) {
$version = Select-CurrentVersion -App $app -Global:$global
} else {
$version = 'current'
}
$dir = versiondir $app $version $global
$json = install_info $app $version $global
if (!$json) {
error "Failed to hold '$app'."
continue
}
$install = @{}
$json | Get-Member -MemberType Properties | ForEach-Object { $install.Add($_.Name, $json.($_.Name)) }
if ($install.hold) {
info "'$app' is already held."
continue
}
$install.hold = $true
save_install_info $install $dir
success "$app is now held and can not be updated anymore."

View File

@@ -2,7 +2,9 @@
# Summary: Opens the app homepage
param($app)
. "$PSScriptRoot\..\lib\versions.ps1" # 'Select-CurrentVersion'
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Get-Manifest'
. "$PSScriptRoot\..\lib\download.ps1" # 'Get-UserAgent'
if ($app) {
$null, $manifest, $bucket, $null = Get-Manifest $app

View File

@@ -34,20 +34,19 @@ foreach ($item in $import.buckets) {
}
foreach ($item in $import.apps) {
$instArgs = @()
$holdArgs = @()
$info = $item.Info -Split ', '
$global = if ('Global install' -in $info) {
' --global'
} else {
''
if ('Global install' -in $info) {
$instArgs += '--global'
$holdArgs += '--global'
}
$arch = if ('64bit' -in $info -and '64bit' -ne $def_arch) {
' --arch 64bit'
if ('64bit' -in $info -and '64bit' -ne $def_arch) {
$instArgs += '--arch', '64bit'
} elseif ('32bit' -in $info -and '32bit' -ne $def_arch) {
' --arch 32bit'
$instArgs += '--arch', '32bit'
} elseif ('arm64' -in $info -and 'arm64' -ne $def_arch) {
' --arch arm64'
} else {
''
$instArgs += '--arch', 'arm64'
}
$app = if ($item.Source -in $bucket_names) {
@@ -58,9 +57,9 @@ foreach ($item in $import.apps) {
$item.Source
}
& "$PSScriptRoot\scoop-install.ps1" $app$global$arch
& "$PSScriptRoot\scoop-install.ps1" $app @instArgs
if ('Held package' -in $info) {
& "$PSScriptRoot\scoop-hold.ps1" $($item.Name)$global
& "$PSScriptRoot\scoop-hold.ps1" $item.Name @holdArgs
}
}

View File

@@ -1,13 +1,15 @@
# Usage: scoop info <app> [--verbose]
# Usage: scoop info <app> [options]
# Summary: Display information about an app
# Options:
# -v, --verbose Show full paths and URLs
# Help: Options:
# -v, --verbose Show full paths and URLs
. "$PSScriptRoot\..\lib\getopt.ps1"
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Get-Manifest'
. "$PSScriptRoot\..\lib\versions.ps1" # 'Get-InstalledVersion'
. "$PSScriptRoot\..\lib\versions.ps1" # 'Get-InstalledVersion', 'Select-CurrentVersion'
. "$PSScriptRoot\..\lib\download.ps1" # 'Get-RemoteFileSize'
$opt, $app, $err = getopt $args 'v' 'verbose'
$original_app = $app
if ($err) { error "scoop info: $err"; exit 1 }
$verbose = $opt.v -or $opt.verbose
@@ -22,7 +24,7 @@ if (!$manifest) {
$global = installed $app $true
$status = app_status $app $global
$install = install_info $app $status.version $global
$status.installed = $bucket -and $install.bucket -eq $bucket
$status.installed = ($bucket -and $install.bucket -eq $bucket) -or (installed $app)
$version_output = $manifest.version
$manifest_file = if ($bucket) {
manifest_path $app $bucket
@@ -30,12 +32,30 @@ $manifest_file = if ($bucket) {
$url
}
# Standalone and Source detection
if ((Test-Path $original_app) -or ($original_app -match '^(ht|f)tps?://|\\\\')) {
$standalone = $true
if (Test-Path $original_app) {
$original_app = (Get-AbsolutePath "$original_app")
}
if ($install.url) {
if (Test-Path $install.url) {
$install_url = (Get-AbsolutePath $install.url)
} else {
$install_url = $install.url
}
}
if ($original_app -eq $install_url) {
$same_source = $true
}
}
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>"
$dir, $original_dir, $persist_dir = '<root>', '<root>', '<root>'
}
if ($status.installed) {
@@ -43,21 +63,39 @@ if ($status.installed) {
if ($install.url) {
$manifest_file = $install.url
}
if ($status.version -eq $manifest.version) {
if ($status.deprecated) {
$manifest_file = $status.deprecated
} elseif ($standalone -and !$same_source) {
$version_output = $manifest.version
} elseif ($status.version -eq $manifest.version) {
$version_output = $status.version
} else {
$version_output = "$($status.version) (Update to $($manifest.version) available)"
}
}
$item = [ordered]@{ Name = $app }
if ($status.deprecated) {
$item.Name += ' (DEPRECATED)'
}
if ($manifest.description) {
$item.Description = $manifest.description
}
$item.Version = $version_output
if ($bucket) {
$item.Bucket = $bucket
$item.Source = if ($standalone) {
$original_app
} else {
if ($install.bucket) {
$install.bucket
} elseif ($install.url) {
$install.url
} else {
$bucket
}
}
if ($manifest.homepage) {
$item.Website = $manifest.homepage.TrimEnd('/')
}
@@ -69,7 +107,7 @@ if ($manifest.license) {
$manifest.license
} elseif ($manifest.license -match '[|,]') {
if ($verbose) {
"$($manifest.license) ($(($manifest.license -Split "\||," | ForEach-Object { "https://spdx.org/licenses/$_.html" }) -join ', '))"
"$($manifest.license) ($(($manifest.license -Split '\||,' | ForEach-Object { "https://spdx.org/licenses/$_.html" }) -join ', '))"
} else {
$manifest.license
}
@@ -84,7 +122,7 @@ if ($manifest.depends) {
if (Test-Path $manifest_file) {
if (Get-Command git -ErrorAction Ignore) {
$gitinfo = (Invoke-Git -Path (Split-Path $manifest_file) -ArgumentList @('log', '-1', '-s', "--format='%aD#%an'", $manifest_file) 2> $null) -Split '#'
$gitinfo = (Invoke-Git -Path (Split-Path $manifest_file) -ArgumentList @('log', '-1', '-s', '--format=%aI#%an', $manifest_file) 2> $null) -Split '#'
}
if ($gitinfo) {
$item.'Updated at' = $gitinfo[0] | Get-Date
@@ -100,11 +138,13 @@ if ($verbose) { $item.Manifest = $manifest_file }
if ($status.installed) {
# Show installed versions
$installed_output = @()
Get-InstalledVersion -AppName $app -Global:$global | ForEach-Object {
$installed_output += if ($verbose) { versiondir $app $_ $global } else { "$_$(if ($global) { " *global*" })" }
if (!$standalone -or $same_source) {
$installed_output = @()
Get-InstalledVersion -AppName $app -Global:$global | ForEach-Object {
$installed_output += if ($verbose) { versiondir $app $_ $global } else { "$_$(if ($global) { ' *global*' })" }
}
$item.Installed = $installed_output -join "`n"
}
$item.Installed = $installed_output -join "`n"
if ($verbose) {
# Show size of installation
@@ -112,7 +152,7 @@ if ($status.installed) {
# Collect file list from each location
$appFiles = Get-ChildItem $appsdir -Filter $app
$currentFiles = Get-ChildItem $appFiles -Filter (Select-CurrentVersion $app $global)
$currentFiles = Get-ChildItem $appFiles.FullName -Filter (Select-CurrentVersion $app $global)
$persistFiles = Get-ChildItem $persist_dir -ErrorAction Ignore # Will fail if app does not persist data
$cacheFiles = Get-ChildItem $cachedir -Filter "$app#*"
@@ -120,7 +160,7 @@ if ($status.installed) {
$fileTotals = @()
foreach ($fileType in ($appFiles, $currentFiles, $persistFiles, $cacheFiles)) {
if ($null -ne $fileType) {
$fileSum = (Get-ChildItem $fileType -Recurse | Measure-Object -Property Length -Sum).Sum
$fileSum = (Get-ChildItem $fileType.FullName -Recurse -File | Measure-Object -Property Length -Sum).Sum
$fileTotals += coalesce $fileSum 0
} else {
$fileTotals += 0
@@ -160,13 +200,13 @@ if ($status.installed) {
$totalPackage = 0
foreach ($url in @(url $manifest (Get-DefaultArchitecture))) {
try {
if (Test-Path (fullpath (cache_path $app $manifest.version $url))) {
$cached = " (latest version is cached)"
if (Test-Path (cache_path $app $manifest.version $url)) {
$cached = ' (latest version is cached)'
} else {
$cached = $null
}
[int]$urlLength = (Invoke-WebRequest $url -Method Head).Headers.'Content-Length'[0]
$urlLength = Get-RemoteFileSize $url
$totalPackage += $urlLength
} catch [System.Management.Automation.RuntimeException] {
$totalPackage = 0
@@ -196,7 +236,7 @@ if ($binaries) {
$binary_output += $_
}
}
$item.Binaries = $binary_output -join " | "
$item.Binaries = $binary_output -join ' | '
}
$shortcuts = @(arch_specific 'shortcuts' $manifest $install.architecture)
if ($shortcuts) {
@@ -204,13 +244,13 @@ if ($shortcuts) {
$shortcuts | ForEach-Object {
$shortcut_output += $_[1]
}
$item.Shortcuts = $shortcut_output -join " | "
$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 })"
$env_vars += "$($_.name) = $(substitute $env_set.$($_.name) @{ '$dir' = $dir })"
}
$item.Environment = $env_vars -join "`n"
}

View File

@@ -10,33 +10,44 @@
# To install an app from a manifest at a URL:
# scoop install https://raw.githubusercontent.com/ScoopInstaller/Main/master/bucket/runat.json
#
# To install a different version of the app from a URL:
# scoop install https://raw.githubusercontent.com/ScoopInstaller/Main/master/bucket/neovim.json@0.9.0
#
# To install an app from a manifest on your computer
# scoop install \path\to\app.json
#
# To install an app from a manifest on your computer
# scoop install \path\to\app.json@version
#
# Options:
# -g, --global Install the app globally
# -i, --independent Don't install dependencies automatically
# -k, --no-cache Don't use the download cache
# -s, --skip-hash-check Skip hash validation (use with caution!)
# -u, --no-update-scoop Don't update Scoop before installing if it's outdated
# -s, --skip Skip hash validation (use with caution!)
# -a, --arch <32bit|64bit|arm64> Use the specified architecture, if the app supports it
. "$PSScriptRoot\..\lib\getopt.ps1"
. "$PSScriptRoot\..\lib\json.ps1" # 'autoupdate.ps1' 'manifest.ps1' (indirectly)
. "$PSScriptRoot\..\lib\autoupdate.ps1" # 'generate_user_manifest' (indirectly)
. "$PSScriptRoot\..\lib\manifest.ps1" # 'generate_user_manifest' 'Get-Manifest' 'Select-CurrentVersion' (indirectly)
. "$PSScriptRoot\..\lib\system.ps1"
. "$PSScriptRoot\..\lib\install.ps1"
. "$PSScriptRoot\..\lib\download.ps1"
. "$PSScriptRoot\..\lib\decompress.ps1"
. "$PSScriptRoot\..\lib\shortcuts.ps1"
. "$PSScriptRoot\..\lib\psmodules.ps1"
. "$PSScriptRoot\..\lib\versions.ps1"
. "$PSScriptRoot\..\lib\depends.ps1"
if (get_config USE_SQLITE_CACHE) {
. "$PSScriptRoot\..\lib\database.ps1"
}
$opt, $apps, $err = getopt $args 'gikusa:' 'global', 'independent', 'no-cache', 'no-update-scoop', 'skip', 'arch='
$opt, $apps, $err = getopt $args 'giksua:' 'global', 'independent', 'no-cache', 'skip-hash-check', 'no-update-scoop', 'arch='
if ($err) { "scoop install: $err"; exit 1 }
$global = $opt.g -or $opt.global
$check_hash = !($opt.s -or $opt.skip)
$check_hash = !($opt.s -or $opt.'skip-hash-check')
$independent = $opt.i -or $opt.independent
$use_cache = !($opt.k -or $opt.'no-cache')
$architecture = Get-DefaultArchitecture
@@ -56,7 +67,7 @@ if (is_scoop_outdated) {
if ($opt.u -or $opt.'no-update-scoop') {
warn "Scoop is out of date."
} else {
scoop update
& "$PSScriptRoot\scoop-update.ps1"
}
}
@@ -81,7 +92,7 @@ $specific_versions = $apps | Where-Object {
}
# compare object does not like nulls
if ($specific_versions.length -gt 0) {
if ($specific_versions.Count -gt 0) {
$difference = Compare-Object -ReferenceObject $apps -DifferenceObject $specific_versions -PassThru
} else {
$difference = $apps
@@ -90,13 +101,13 @@ if ($specific_versions.length -gt 0) {
$specific_versions_paths = $specific_versions | ForEach-Object {
$app, $bucket, $version = parse_app $_
if (installed_manifest $app $version) {
warn "'$app' ($version) is already installed.`nUse 'scoop update $app$(if ($global) { " --global" })' 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
}
$apps = @(($specific_versions_paths + $difference) | Where-Object { $_ } | Sort-Object -Unique)
$apps = @((@($specific_versions_paths) + $difference) | Where-Object { $_ } | Select-Object -Unique)
# remember which were explictly requested so that we can
# differentiate after dependencies are added

View File

@@ -5,8 +5,9 @@ param($query)
. "$PSScriptRoot\..\lib\versions.ps1" # 'Select-CurrentVersion'
. "$PSScriptRoot\..\lib\manifest.ps1" # 'parse_json' 'Select-CurrentVersion' (indirectly)
. "$PSScriptRoot\..\lib\download.ps1" # 'Get-UserAgent'
$def_arch = Get-DefaultArchitecture
$defaultArchitecture = Get-DefaultArchitecture
if (-not (Get-FormatData ScoopApps)) {
Update-FormatData "$PSScriptRoot\..\supporting\formats\ScoopTypes.Format.ps1xml"
}
@@ -47,10 +48,11 @@ $apps | Where-Object { !$query -or ($_.name -match $query) } | ForEach-Object {
$item.Updated = $updated
$info = @()
if ((app_status $app $global).deprecated) { $info += 'Deprecated package'}
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) {
if ($install_info.architecture -and $defaultArchitecture -ne $install_info.architecture) {
$info += $install_info.architecture
}
$item.Info = $info -join ', '

View File

@@ -8,6 +8,7 @@
. "$PSScriptRoot\..\lib\getopt.ps1"
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Select-CurrentVersion' (indirectly)
. "$PSScriptRoot\..\lib\system.ps1" # 'env_add_path' (indirectly)
. "$PSScriptRoot\..\lib\install.ps1"
. "$PSScriptRoot\..\lib\versions.ps1" # 'Select-CurrentVersion'
. "$PSScriptRoot\..\lib\shortcuts.ps1"
@@ -69,7 +70,7 @@ $apps | ForEach-Object {
#region Workaround for #2952
if (test_running_process $app $global) {
continue
return
}
#endregion Workaround for #2952
@@ -79,8 +80,11 @@ $apps | ForEach-Object {
$dir = link_current $dir
create_shims $manifest $dir $global $architecture
create_startmenu_shortcuts $manifest $dir $global $architecture
# unset all potential old env before re-adding
env_rm_path $manifest $dir $global $architecture
env_rm $manifest $global $architecture
env_add_path $manifest $dir $global $architecture
env_set $manifest $dir $global $architecture
env_set $manifest $global $architecture
# unlink all potential old link before re-persisting
unlink_persist_data $manifest $original_dir
persist_data $manifest $original_dir $persist_dir

View File

@@ -3,25 +3,16 @@
# Help: Searches for apps that are available to install.
#
# If used with [query], shows app names that match the query.
# - With 'use_sqlite_cache' enabled, [query] is partially matched against app names, binaries, and shortcuts.
# - Without 'use_sqlite_cache', [query] can be a regular expression to match against app names and binaries.
# Without [query], shows all the available apps.
param($query)
. "$PSScriptRoot\..\lib\manifest.ps1" # 'manifest'
. "$PSScriptRoot\..\lib\versions.ps1" # 'Get-LatestVersion'
. "$PSScriptRoot\..\lib\download.ps1"
$list = @()
try {
$query = New-Object Regex $query, 'IgnoreCase'
} catch {
abort "Invalid regular expression: $($_.Exception.InnerException.Message)"
}
$githubtoken = Get-GitHubToken
$authheader = @{}
if ($githubtoken) {
$authheader = @{'Authorization' = "token $githubtoken"}
}
$list = [System.Collections.Generic.List[PSCustomObject]]::new()
function bin_match($manifest, $query) {
if (!$manifest.bin) { return $false }
@@ -32,41 +23,98 @@ function bin_match($manifest, $query) {
if ((strip_ext $fname) -match $query) { $fname }
elseif ($alias -match $query) { $alias }
}
if ($bins) { return $bins }
else { return $false }
}
function bin_match_json($json, $query) {
[System.Text.Json.JsonElement]$bin = [System.Text.Json.JsonElement]::new()
if (!$json.RootElement.TryGetProperty('bin', [ref] $bin)) { return $false }
$bins = @()
if ($bin.ValueKind -eq [System.Text.Json.JsonValueKind]::String -and [System.IO.Path]::GetFileNameWithoutExtension($bin) -match $query) {
$bins += [System.IO.Path]::GetFileName($bin)
} elseif ($bin.ValueKind -eq [System.Text.Json.JsonValueKind]::Array) {
foreach ($subbin in $bin.EnumerateArray()) {
if ($subbin.ValueKind -eq [System.Text.Json.JsonValueKind]::String -and [System.IO.Path]::GetFileNameWithoutExtension($subbin) -match $query) {
$bins += [System.IO.Path]::GetFileName($subbin)
} elseif ($subbin.ValueKind -eq [System.Text.Json.JsonValueKind]::Array) {
if ([System.IO.Path]::GetFileNameWithoutExtension($subbin[0]) -match $query) {
$bins += [System.IO.Path]::GetFileName($subbin[0])
} elseif ($subbin.GetArrayLength() -ge 2 -and $subbin[1] -match $query) {
$bins += $subbin[1]
}
}
}
}
if ($bins) { return $bins }
else { return $false }
}
function search_bucket($bucket, $query) {
$apps = apps_in_bucket (Find-BucketDirectory $bucket) | ForEach-Object { @{ name = $_ } }
$apps = Get-ChildItem (Find-BucketDirectory $bucket) -Filter '*.json' -Recurse
if ($query) {
$apps = $apps | Where-Object {
if ($_.name -match $query) { return $true }
$bin = bin_match (manifest $_.name $bucket) $query
$apps | ForEach-Object {
$filepath = $_.FullName
$json = try {
[System.Text.Json.JsonDocument]::Parse([System.IO.File]::ReadAllText($filepath))
} catch {
debug "Failed to parse manifest file: $filepath (error: $_)"
return
}
$name = $_.BaseName
if ($name -match $query) {
$list.Add([PSCustomObject]@{
Name = $name
Version = $json.RootElement.GetProperty('version')
Source = $bucket
Binaries = ''
})
} else {
$bin = bin_match_json $json $query
if ($bin) {
$_.bin = $bin
return $true
$list.Add([PSCustomObject]@{
Name = $name
Version = $json.RootElement.GetProperty('version')
Source = $bucket
Binaries = $bin -join ' | '
})
}
}
}
$apps | ForEach-Object { $_.version = (Get-LatestVersion -AppName $_.name -Bucket $bucket); $_ }
}
function download_json($url) {
$ProgressPreference = 'SilentlyContinue'
$result = Invoke-WebRequest $url -UseBasicParsing -Headers $authheader | Select-Object -ExpandProperty content | ConvertFrom-Json
$ProgressPreference = 'Continue'
$result
}
# fallback function for PowerShell 5
function search_bucket_legacy($bucket, $query) {
$apps = Get-ChildItem (Find-BucketDirectory $bucket) -Filter '*.json' -Recurse
function github_ratelimit_reached {
$api_link = 'https://api.github.com/rate_limit'
$ret = (download_json $api_link).rate.remaining -eq 0
if ($ret) {
Write-Host "GitHub API rate limit reached.
Please try again later or configure your API token using 'scoop config gh_token <your token>'."
$apps | ForEach-Object {
$manifest = [System.IO.File]::ReadAllText($_.FullName) | ConvertFrom-Json -ErrorAction Continue
$name = $_.BaseName
if ($name -match $query) {
$list.Add([PSCustomObject]@{
Name = $name
Version = $manifest.Version
Source = $bucket
Binaries = ''
})
} else {
$bin = bin_match $manifest $query
if ($bin) {
$list.Add([PSCustomObject]@{
Name = $name
Version = $manifest.Version
Source = $bucket
Binaries = $bin -join ' | '
})
}
}
}
$ret
}
function search_remote($bucket, $query) {
@@ -88,7 +136,7 @@ function search_remotes($query) {
$names = $buckets | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty name
$results = $names | Where-Object { !(Test-Path $(Find-BucketDirectory $_)) } | ForEach-Object {
@{ "bucket" = $_; "results" = (search_remote $_ $query) }
@{ 'bucket' = $_; 'results' = (search_remote $_ $query) }
} | Where-Object { $_.results }
if ($results.count -gt 0) {
@@ -96,46 +144,58 @@ function search_remotes($query) {
(add them using 'scoop bucket add <bucket name>')"
}
$remote_list = @()
$results | ForEach-Object {
$name = $_.bucket
$bucket = $_.bucket
$_.results | ForEach-Object {
$item = [ordered]@{}
$item.Name = $_
$item.Source = $name
$list += [PSCustomObject]$item
$item.Source = $bucket
$remote_list += [PSCustomObject]$item
}
}
$list
$remote_list
}
Get-LocalBucket | ForEach-Object {
$res = search_bucket $_ $query
$local_results = $local_results -or $res
if ($res) {
$name = "$_"
if (get_config USE_SQLITE_CACHE) {
. "$PSScriptRoot\..\lib\database.ps1"
Select-ScoopDBItem $query -From @('name', 'binary', 'shortcut') |
Select-Object -Property name, version, bucket, binary |
ForEach-Object {
$list.Add([PSCustomObject]@{
Name = $_.name
Version = $_.version
Source = $_.bucket
Binaries = $_.binary
})
}
} else {
try {
$query = New-Object Regex $query, 'IgnoreCase'
} catch {
abort "Invalid regular expression: $($_.Exception.InnerException.Message)"
}
$res | ForEach-Object {
$item = [ordered]@{}
$item.Name = $_.name
$item.Version = $_.version
$item.Source = $name
$item.Binaries = ""
if ($_.bin) { $item.Binaries = $_.bin -join ' | ' }
$list += [PSCustomObject]$item
$jsonTextAvailable = [System.AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { [System.IO.Path]::GetFileNameWithoutExtension($_.Location) -eq 'System.Text.Json' }
Get-LocalBucket | ForEach-Object {
if ($jsonTextAvailable) {
search_bucket $_ $query
} else {
search_bucket_legacy $_ $query
}
}
}
if ($list.Length -gt 0) {
Write-Host "Results from local buckets..."
if ($list.Count -gt 0) {
Write-Host 'Results from local buckets...'
$list
}
if (!$local_results -and !(github_ratelimit_reached)) {
if ($list.Count -eq 0 -and !(github_ratelimit_reached)) {
$remote_results = search_remotes $query
if (!$remote_results) {
warn "No matches found."
warn 'No matches found.'
exit 1
}
$remote_results

View File

@@ -12,7 +12,7 @@
#
# To list all shims or matching shims, use the 'list' subcommand:
#
# scoop shim list [<shim_name>/<pattern>...]
# scoop shim list [<regex_pattern>...]
#
# To show a shim's information, use the 'info' subcommand:
#
@@ -35,6 +35,7 @@ param($SubCommand)
. "$PSScriptRoot\..\lib\getopt.ps1"
. "$PSScriptRoot\..\lib\install.ps1" # for rm_shim
. "$PSScriptRoot\..\lib\system.ps1" # 'Add-Path' (indirectly)
if ($SubCommand -notin @('add', 'rm', 'list', 'info', 'alter')) {
if (!$SubCommand) {
@@ -82,7 +83,7 @@ function Get-ShimInfo($ShimPath) {
function Get-ShimPath($ShimName, $Global) {
'.shim', '.ps1' | ForEach-Object {
$shimPath = Join-Path (shimdir $Global) "$ShimName$_"
if (Test-Path $shimPath) {
if (Test-Path -LiteralPath $shimPath) {
return $shimPath
}
}
@@ -144,7 +145,7 @@ switch ($SubCommand) {
$other | ForEach-Object {
try {
$pattern = $_
[Regex]::New($pattern)
[void][Regex]::New($pattern)
} catch {
Write-Host "ERROR: Invalid pattern: " -ForegroundColor Red -NoNewline
Write-Host $pattern -ForegroundColor Magenta

View File

@@ -6,9 +6,10 @@
. "$PSScriptRoot\..\lib\manifest.ps1" # 'manifest' 'parse_json' "install_info"
. "$PSScriptRoot\..\lib\versions.ps1" # 'Select-CurrentVersion'
. "$PSScriptRoot\..\lib\download.ps1" # 'Get-UserAgent'
# check if scoop needs updating
$currentdir = fullpath $(versiondir 'scoop' 'current')
$currentdir = versiondir 'scoop' 'current'
$needs_update = $false
$bucket_needs_update = $false
$script:network_failure = $false
@@ -58,7 +59,7 @@ $true, $false | ForEach-Object { # local and global apps
Get-ChildItem $dir | Where-Object name -NE 'scoop' | ForEach-Object {
$app = $_.name
$status = app_status $app $global
if (!$status.outdated -and !$status.failed -and !$status.removed -and !$status.missing_deps) { return }
if (!$status.outdated -and !$status.failed -and !$status.deprecated -and !$status.removed -and !$status.missing_deps) { return }
$item = [ordered]@{}
$item.Name = $app
@@ -66,9 +67,10 @@ $true, $false | ForEach-Object { # local and global apps
$item.'Latest Version' = if ($status.outdated) { $status.latest_version } else { "" }
$item.'Missing Dependencies' = $status.missing_deps -Split ' ' -Join ' | '
$info = @()
if ($status.failed) { $info += 'Install failed' }
if ($status.hold) { $info += 'Held package' }
if ($status.removed) { $info += 'Manifest removed' }
if ($status.failed) { $info += 'Install failed' }
if ($status.hold) { $info += 'Held package' }
if ($status.deprecated) { $info += 'Deprecated' }
if ($status.removed) { $info += 'Manifest removed' }
$item.Info = $info -join ', '
$list += [PSCustomObject]$item
}

View File

@@ -53,8 +53,16 @@ $apps | ForEach-Object {
}
$dir = versiondir $app $version $global
$json = install_info $app $version $global
if (!$json) {
error "Failed to unhold '$app'"
continue
}
$install = @{}
$json | Get-Member -MemberType Properties | ForEach-Object { $install.Add($_.Name, $json.($_.Name)) }
if (!$install.hold) {
info "'$app' is not held."
continue
}
$install.hold = $null
save_install_info $install $dir
success "$app is no longer held and can be updated again."

View File

@@ -8,6 +8,7 @@
. "$PSScriptRoot\..\lib\getopt.ps1"
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Get-Manifest' 'Select-CurrentVersion' (indirectly)
. "$PSScriptRoot\..\lib\system.ps1"
. "$PSScriptRoot\..\lib\install.ps1"
. "$PSScriptRoot\..\lib\shortcuts.ps1"
. "$PSScriptRoot\..\lib\psmodules.ps1"
@@ -73,7 +74,7 @@ if (!$apps) { exit 0 }
continue
}
run_uninstaller $manifest $architecture $dir
Invoke-Installer -Path $dir -Manifest $manifest -ProcessorArchitecture $architecture -Global $global -Uninstall
rm_shims $app $manifest $global $architecture
rm_startmenu_shortcuts $manifest $global $architecture

View File

@@ -6,16 +6,17 @@
# You can use '*' in place of <app> to update all apps.
#
# Options:
# -f, --force Force update even when there isn't a newer version
# -g, --global Update a globally installed app
# -i, --independent Don't install dependencies automatically
# -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 '*')
# -f, --force Force update even when there isn't a newer version
# -g, --global Update a globally installed app
# -i, --independent Don't install dependencies automatically
# -k, --no-cache Don't use the download cache
# -s, --skip-hash-check Skip hash validation (use with caution!)
# -q, --quiet Hide extraneous messages
# -a, --all Update all apps (alternative to '*')
. "$PSScriptRoot\..\lib\getopt.ps1"
. "$PSScriptRoot\..\lib\json.ps1" # 'save_install_info' in 'manifest.ps1' (indirectly)
. "$PSScriptRoot\..\lib\system.ps1"
. "$PSScriptRoot\..\lib\shortcuts.ps1"
. "$PSScriptRoot\..\lib\psmodules.ps1"
. "$PSScriptRoot\..\lib\decompress.ps1"
@@ -23,12 +24,16 @@
. "$PSScriptRoot\..\lib\versions.ps1"
. "$PSScriptRoot\..\lib\depends.ps1"
. "$PSScriptRoot\..\lib\install.ps1"
. "$PSScriptRoot\..\lib\download.ps1"
if (get_config USE_SQLITE_CACHE) {
. "$PSScriptRoot\..\lib\database.ps1"
}
$opt, $apps, $err = getopt $args 'gfiksqa' 'global', 'force', 'independent', 'no-cache', 'skip', 'quiet', 'all'
$opt, $apps, $err = getopt $args 'gfiksqa' 'global', 'force', 'independent', 'no-cache', 'skip-hash-check', 'quiet', 'all'
if ($err) { "scoop update: $err"; exit 1 }
$global = $opt.g -or $opt.global
$force = $opt.f -or $opt.force
$check_hash = !($opt.s -or $opt.skip)
$check_hash = !($opt.s -or $opt.'skip-hash-check')
$use_cache = !($opt.k -or $opt.'no-cache')
$quiet = $opt.q -or $opt.quiet
$independent = $opt.i -or $opt.independent
@@ -37,21 +42,21 @@ $all = $opt.a -or $opt.all
# load config
$configRepo = get_config SCOOP_REPO
if (!$configRepo) {
$configRepo = "https://github.com/ScoopInstaller/Scoop"
$configRepo = 'https://github.com/ScoopInstaller/Scoop'
set_config SCOOP_REPO $configRepo | Out-Null
}
# Find current update channel from config
$configBranch = get_config SCOOP_BRANCH
if (!$configBranch) {
$configBranch = "master"
$configBranch = 'master'
set_config SCOOP_BRANCH $configBranch | Out-Null
}
if(($PSVersionTable.PSVersion.Major) -lt 5) {
if (($PSVersionTable.PSVersion.Major) -lt 5) {
# check powershell version
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"
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
}
$show_update_log = get_config SHOW_UPDATE_LOG $true
@@ -62,15 +67,15 @@ function Sync-Scoop {
[Switch]$Log
)
# Test if Scoop Core is hold
if(Test-ScoopCoreOnHold) {
if (Test-ScoopCoreOnHold) {
return
}
# check for git
if (!(Test-GitAvailable)) { abort "Scoop uses Git to update itself. Run 'scoop install git' and try again." }
Write-Host "Updating Scoop..."
$currentdir = fullpath $(versiondir 'scoop' 'current')
Write-Host 'Updating Scoop...'
$currentdir = versiondir 'scoop' 'current'
if (!(Test-Path "$currentdir\.git")) {
$newdir = "$currentdir\..\new"
$olddir = "$currentdir\..\old"
@@ -89,7 +94,7 @@ function Sync-Scoop {
Rename-Item $newdir 'current' -ErrorAction Stop
} catch {
Write-Warning $_
abort "Scoop update failed. Folder in use. Paste $newdir into $currentdir."
abort "Scoop update failed. Folder in use. Please rename folders $currentdir to ``old`` and $newdir to ``current``."
}
}
} else {
@@ -107,10 +112,10 @@ function Sync-Scoop {
# Stash uncommitted changes
if (Invoke-Git -Path $currentdir -ArgumentList @('diff', 'HEAD', '--name-only')) {
if (get_config AUTOSTASH_ON_CONFLICT) {
warn "Uncommitted changes detected. Stashing..."
warn 'Uncommitted changes detected. Stashing...'
Invoke-Git -Path $currentdir -ArgumentList @('stash', 'push', '-m', "WIP at $([System.DateTime]::Now.ToString('o'))", '-u', '-q')
} else {
warn "Uncommitted changes detected. Update aborted."
warn 'Uncommitted changes detected. Update aborted.'
return
}
}
@@ -151,7 +156,7 @@ function Sync-Bucket {
Param (
[Switch]$Log
)
Write-Host "Updating Buckets..."
Write-Host 'Updating Buckets...'
if (!(Test-Path (Join-Path (Find-BucketDirectory 'main' -Root) '.git'))) {
info "Converting 'main' bucket to git repo..."
@@ -169,40 +174,88 @@ function Sync-Bucket {
$buckets = Get-LocalBucket | ForEach-Object {
$path = Find-BucketDirectory $_ -Root
return @{
name = $_
name = $_
valid = Test-Path (Join-Path $path '.git')
path = $path
path = $path
}
}
$buckets | Where-Object { !$_.valid } | ForEach-Object { Write-Host "'$($_.name)' is not a git repository. Skipped." }
$updatedFiles = [System.Collections.ArrayList]::Synchronized([System.Collections.ArrayList]::new())
$removedFiles = [System.Collections.ArrayList]::Synchronized([System.Collections.ArrayList]::new())
if ($PSVersionTable.PSVersion.Major -ge 7) {
# Parallel parameter is available since PowerShell 7
$buckets | Where-Object { $_.valid } | ForEach-Object -ThrottleLimit 5 -Parallel {
. "$using:PSScriptRoot\..\lib\core.ps1"
. "$using:PSScriptRoot\..\lib\buckets.ps1"
$bucketLoc = $_.path
$name = $_.name
$bucketLoc = $_.path
$innerBucketLoc = Find-BucketDirectory $name
$previousCommit = Invoke-Git -Path $bucketLoc -ArgumentList @('rev-parse', 'HEAD')
Invoke-Git -Path $bucketLoc -ArgumentList @('pull', '-q')
if ($using:Log) {
Invoke-GitLog -Path $bucketLoc -Name $name -CommitHash $previousCommit
}
if (get_config USE_SQLITE_CACHE) {
Invoke-Git -Path $bucketLoc -ArgumentList @('diff', '--name-status', $previousCommit) | ForEach-Object {
$status, $file = $_ -split '\s+', 2
$filePath = Join-Path $bucketLoc $file
if ($filePath -match "^$([regex]::Escape($innerBucketLoc)).*\.json$") {
switch ($status) {
{ $_ -in 'A', 'M', 'R' } {
[void]($using:updatedFiles).Add($filePath)
}
'D' {
[void]($using:removedFiles).Add([pscustomobject]@{
Name = ([System.IO.FileInfo]$file).BaseName
Bucket = $name
})
}
}
}
}
}
}
} else {
$buckets | Where-Object { $_.valid } | ForEach-Object {
$bucketLoc = $_.path
$name = $_.name
$bucketLoc = $_.path
$innerBucketLoc = Find-BucketDirectory $name
$previousCommit = Invoke-Git -Path $bucketLoc -ArgumentList @('rev-parse', 'HEAD')
Invoke-Git -Path $bucketLoc -ArgumentList @('pull', '-q')
if ($Log) {
Invoke-GitLog -Path $bucketLoc -Name $name -CommitHash $previousCommit
}
if (get_config USE_SQLITE_CACHE) {
Invoke-Git -Path $bucketLoc -ArgumentList @('diff', '--name-status', $previousCommit) | ForEach-Object {
$status, $file = $_ -split '\s+', 2
$filePath = Join-Path $bucketLoc $file
if ($filePath -match "^$([regex]::Escape($innerBucketLoc)).*\.json$") {
switch ($status) {
{ $_ -in 'A', 'M', 'R' } {
[void]($updatedFiles).Add($filePath)
}
'D' {
[void]($removedFiles).Add([pscustomobject]@{
Name = ([System.IO.FileInfo]$file).BaseName
Bucket = $name
})
}
}
}
}
}
}
}
if ((get_config USE_SQLITE_CACHE) -and ($updatedFiles.Count -gt 0 -or $removedFiles.Count -gt 0)) {
info 'Updating cache...'
Set-ScoopDB -Path $updatedFiles
$removedFiles | Remove-ScoopDBItem
}
}
function update($app, $global, $quiet = $false, $independent, $suggested, $use_cache = $true, $check_hash = $true) {
@@ -240,10 +293,17 @@ function update($app, $global, $quiet = $false, $independent, $suggested, $use_c
Write-Host "Updating '$app' ($old_version -> $version)"
#region Workaround for #2952
if (test_running_process $app $global) {
Write-Host 'Running process detected, skip updating.'
return
}
#endregion Workaround for #2952
# region Workaround
# 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"
Write-Host 'Downloading new version'
if (Test-Aria2Enabled) {
Invoke-CachedAria2Download $app $version $manifest $architecture $cachedir $manifest.cookie $true $check_hash
} else {
@@ -254,19 +314,19 @@ function update($app, $global, $quiet = $false, $independent, $suggested, $use_c
if ($check_hash) {
$manifest_hash = hash_for_url $manifest $url $architecture
$source = fullpath (cache_path $app $version $url)
$source = cache_path $app $version $url
$ok, $err = check_hash $source $manifest_hash $(show_app $app $bucket)
if (!$ok) {
error $err
if (Test-Path $source) {
# rm cached file
Remove-Item -force $source
Remove-Item -Force $source
}
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')
}
}
}
@@ -280,24 +340,17 @@ function update($app, $global, $quiet = $false, $independent, $suggested, $use_c
Invoke-HookScript -HookType 'pre_uninstall' -Manifest $old_manifest -Arch $architecture
#region Workaround for #2952
if (test_running_process $app $global) {
return
}
#endregion Workaround for #2952
Write-Host "Uninstalling '$app' ($old_version)"
run_uninstaller $old_manifest $architecture $dir
Invoke-Installer -Path $dir -Manifest $old_manifest -ProcessorArchitecture $architecture -Uninstall
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
# directory.
$refdir = unlink_current $dir
uninstall_psmodule $old_manifest $refdir $global
env_rm_path $old_manifest $refdir $global $architecture
env_rm $old_manifest $global $architecture
if ($force -and ($old_version -eq $version)) {
if (!(Test-Path "$dir/../_$version.old")) {
@@ -403,11 +456,11 @@ if (-not ($apps -or $all)) {
} elseif ($outdated.Length -eq 0) {
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:'
}
}
$suggested = @{};
$suggested = @{}
# $outdated is a list of ($app, $global) tuples
$outdated | ForEach-Object { update @_ $quiet $independent $suggested $use_cache $check_hash }
}

View File

@@ -29,29 +29,28 @@
# -p, --passthru Return reports as objects
. "$PSScriptRoot\..\lib\getopt.ps1"
. "$PSScriptRoot\..\lib\versions.ps1" # 'Select-CurrentVersion'
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Get-Manifest'
. "$PSScriptRoot\..\lib\json.ps1" # 'json_path'
. "$PSScriptRoot\..\lib\install.ps1" # 'hash_for_url'
. "$PSScriptRoot\..\lib\download.ps1" # 'hash_for_url'
. "$PSScriptRoot\..\lib\depends.ps1" # 'Get-Dependency'
$opt, $apps, $err = getopt $args 'asnup' @('all', 'scan', 'no-depends', 'no-update-scoop', 'passthru')
if ($err) { "scoop virustotal: $err"; exit 1 }
if (!$apps) { my_usage; exit 1 }
$architecture = Format-ArchitectureString
$all = $apps -eq '*' -or $opt.a -or $opt.all
if (!$apps -and !$all) { my_usage; exit 1 }
$architecture = Get-DefaultArchitecture
if (is_scoop_outdated) {
if ($opt.u -or $opt.'no-update-scoop') {
warn 'Scoop is out of date.'
} else {
scoop update
& "$PSScriptRoot\scoop-update.ps1"
}
}
$apps_param = $apps
if ($apps_param -eq '*' -or $opt.a -or $opt.all) {
$apps = installed_apps $false
$apps += installed_apps $true
if ($all) {
$apps = (installed_apps $false) + (installed_apps $true)
}
if (!$opt.n -and !$opt.'no-depends') {
@@ -88,11 +87,6 @@ Function ConvertTo-VirusTotalUrlId ($url) {
$url_id
}
Function Get-RemoteFileSize ($url) {
$response = Invoke-WebRequest -Uri $url -Method HEAD -UseBasicParsing
$response.Headers.'Content-Length' | ForEach-Object { [System.Convert]::ToInt32($_) }
}
Function Get-VirusTotalResultByHash ($hash, $url, $app) {
$hash = $hash.ToLower()
$api_url = "https://www.virustotal.com/api/v3/files/$hash"
@@ -136,7 +130,7 @@ Function Get-VirusTotalResultByHash ($hash, $url, $app) {
warn "$app`: $unsafe/$total, see $report_url"
}
Default {
warn "`e[31m$app`: $unsafe/$total, see $report_url`e[0m"
warn "$([char]0x1b)[31m$app`: $unsafe/$total, see $report_url$([char]0x1b)[0m"
}
}
$maliciousResults = $vendorResults |

View File

@@ -4,7 +4,7 @@
param($command)
if (!$command) {
'ERROR: <command> missing'
error '<command> missing'
my_usage
exit 1
}
@@ -12,7 +12,7 @@ if (!$command) {
$path = Get-CommandPath $command
if ($null -eq $path) {
Write-Host "'$command' not found / not a scoop shim."
warn "'$command' not found, not a scoop shim, or a broken shim."
exit 2
} else {
friendly_path $path

View File

@@ -127,10 +127,6 @@
"installer": {
"$ref": "#/definitions/installer"
},
"msi": {
"$ref": "#/definitions/stringOrArrayOfStrings",
"description": "Deprecated"
},
"post_install": {
"$ref": "#/definitions/stringOrArrayOfStrings"
},
@@ -603,10 +599,6 @@
"license": {
"$ref": "#/definitions/license"
},
"msi": {
"$ref": "#/definitions/stringOrArrayOfStrings",
"description": "Deprecated"
},
"notes": {
"$ref": "#/definitions/stringOrArrayOfStrings"
},

View File

@@ -1 +0,0 @@
packages/

View File

@@ -1 +0,0 @@
9726c3a429009a5b22bd92cb8ab96724c670e164e7240e83f27b7c8b7bd1ca39 *shim.exe

View File

@@ -1 +0,0 @@
18a737674afde4d5e7e1647d8d1e98471bb260513c57739651f92fdf1647d76c92f0cd0a9bb458daf4eae4bdab9d31404162acf6d74a041e6415752b75d722e0 *shim.exe

Binary file not shown.

View File

@@ -1,22 +0,0 @@
Param([Switch]$Fast)
Push-Location $PSScriptRoot
. "$PSScriptRoot\..\..\lib\core.ps1"
. "$PSScriptRoot\..\..\lib\install.ps1"
if (!$Fast) {
Write-Host "Install dependencies ..."
& "$PSScriptRoot\install.ps1"
}
$output = "$PSScriptRoot\bin"
Write-Output 'Compiling 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 {
"$((Get-FileHash -Path $_ -Algorithm SHA256).Hash.ToLower()) *$($_.Name)" | Out-File "$PSScriptRoot\bin\checksum.sha256" -Append -Encoding oem
"$((Get-FileHash -Path $_ -Algorithm SHA512).Hash.ToLower()) *$($_.Name)" | Out-File "$PSScriptRoot\bin\checksum.sha512" -Append -Encoding oem
}
Pop-Location

View File

@@ -1,8 +0,0 @@
# https://github.com/edymtt/nugetstandalone
$destinationFolder = "$PSScriptRoot\packages"
if ((Test-Path -Path $destinationFolder)) {
Remove-Item -Path $destinationFolder -Recurse | Out-Null
}
New-Item $destinationFolder -Type Directory | Out-Null
nuget install packages.config -o $destinationFolder -ExcludeVersion

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Net.Compilers.Toolset" version="4.2.0" targetFramework="net45" developmentDependency="true" />
</packages>

View File

@@ -1,168 +0,0 @@
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
namespace Scoop {
class Program {
[DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
static extern bool CreateProcess(string lpApplicationName,
string lpCommandLine, IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes, bool bInheritHandles,
uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory,
[In] ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
const int ERROR_ELEVATION_REQUIRED = 740;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct STARTUPINFO {
public Int32 cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public Int32 dwX;
public Int32 dwY;
public Int32 dwXSize;
public Int32 dwYSize;
public Int32 dwXCountChars;
public Int32 dwYCountChars;
public Int32 dwFillAttribute;
public Int32 dwFlags;
public Int16 wShowWindow;
public Int16 cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_INFORMATION {
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
[DllImport("kernel32.dll", SetLastError=true)]
static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);
const UInt32 INFINITE = 0xFFFFFFFF;
[DllImport("kernel32.dll", SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetExitCodeProcess(IntPtr hProcess, out uint lpExitCode);
static int Main(string[] args) {
var exe = Assembly.GetExecutingAssembly().Location;
var dir = Path.GetDirectoryName(exe);
var name = Path.GetFileNameWithoutExtension(exe);
var configPath = Path.Combine(dir, name + ".shim");
if(!File.Exists(configPath)) {
Console.Error.WriteLine("Couldn't find " + Path.GetFileName(configPath) + " in " + dir);
return 1;
}
var config = Config(configPath);
var path = Get(config, "path");
var add_args = Get(config, "args");
var si = new STARTUPINFO();
var pi = new PROCESS_INFORMATION();
// create command line
var cmd_args = add_args ?? "";
var pass_args = GetArgs(Environment.CommandLine);
if(!string.IsNullOrEmpty(pass_args)) {
if(!string.IsNullOrEmpty(cmd_args)) cmd_args += " ";
cmd_args += pass_args;
}
if(!string.IsNullOrEmpty(cmd_args)) cmd_args = " " + cmd_args;
var cmd = "\"" + path + "\"" + cmd_args;
if(!CreateProcess(null, cmd, IntPtr.Zero, IntPtr.Zero,
bInheritHandles: true,
dwCreationFlags: 0,
lpEnvironment: IntPtr.Zero, // inherit parent
lpCurrentDirectory: null, // inherit parent
lpStartupInfo: ref si,
lpProcessInformation: out pi)) {
var error = Marshal.GetLastWin32Error();
if(error == ERROR_ELEVATION_REQUIRED) {
// Unfortunately, ShellExecute() does not allow us to run program without
// CREATE_NEW_CONSOLE, so we can not replace CreateProcess() completely.
// The good news is we are okay with CREATE_NEW_CONSOLE when we run program with elevation.
Process process = new Process();
process.StartInfo = new ProcessStartInfo(path, cmd_args);
process.StartInfo.UseShellExecute = true;
try {
process.Start();
}
catch(Win32Exception exception) {
return exception.ErrorCode;
}
process.WaitForExit();
return process.ExitCode;
}
return error;
}
WaitForSingleObject(pi.hProcess, INFINITE);
uint exit_code = 0;
GetExitCodeProcess(pi.hProcess, out exit_code);
// Close process and thread handles.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return (int)exit_code;
}
// now uses GetArgs instead
static string Serialize(string[] args) {
return string.Join(" ", args.Select(a => a.Contains(' ') ? '"' + a + '"' : a));
}
// strips the program name from the command line, returns just the arguments
static string GetArgs(string cmdLine) {
if(cmdLine.StartsWith("\"")) {
var endQuote = cmdLine.IndexOf("\" ", 1);
if(endQuote < 0) return "";
return cmdLine.Substring(endQuote + 1);
}
var space = cmdLine.IndexOf(' ');
if(space < 0 || space == cmdLine.Length - 1) return "";
return cmdLine.Substring(space + 1);
}
static string Get(Dictionary<string, string> dic, string key) {
string value = null;
dic.TryGetValue(key, out value);
return value;
}
static Dictionary<string, string> Config(string path) {
var config = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach(var line in File.ReadAllLines(path)) {
var m = Regex.Match(line, @"([^=]+)=(.*)");
if(m.Success) {
config[m.Groups[1].Value.Trim()] = m.Groups[2].Value.Trim();
}
}
return config;
}
}
}

View File

@@ -1,36 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="packages\Microsoft.Net.Compilers.Toolset.4.2.0\build\Microsoft.Net.Compilers.Toolset.props" Condition="Exists('packages\Microsoft.Net.Compilers.Toolset.4.2.0\build\Microsoft.Net.Compilers.Toolset.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{381F9D2E-2355-4F84-9206-06BB9175F97B}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>Scoop.Shim</RootNamespace>
<AssemblyName>Scoop.Shim</AssemblyName>
<TargetFrameworkVersion>v4.5.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="shim.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('packages\Microsoft.Net.Compilers.Toolset.4.2.0\build\Microsoft.Net.Compilers.Toolset.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.Net.Compilers.Toolset.4.2.0\build\Microsoft.Net.Compilers.Toolset.props'))" />
</Target>
</Project>

View File

@@ -1,10 +0,0 @@
# https://github.com/edymtt/nugetstandalone
$destinationFolder = "$PSScriptRoot\packages"
if (!(Test-Path -Path $destinationFolder)) {
Write-Host -f Red "Run .\install.ps1 first!"
exit 1
}
nuget update packages.config -r $destinationFolder
Remove-Item $destinationFolder -Force -Recurse | Out-Null
nuget install packages.config -o $destinationFolder -ExcludeVersion

View File

@@ -1,2 +0,0 @@
*.zip
*.bak

View File

@@ -1,52 +0,0 @@
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

View File

@@ -1 +1 @@
aa685053f4a5c0e7145f2a27514c8a56ceae25b0824062326f04037937caa558 bin/shim.exe
140e3801d8adeda639a21b14e62b93a4c7d26b7a758421f43c82be59753be49b *shim.exe

View File

@@ -1 +1 @@
67c605c8163869d8ef8153c64eb09b82645cbae8228928c0fef944d0259a7b2d3791ecf4b4b01e23566916a878ee7977bfc1a59846bccf3c63bd6a1cf4f521b5 bin/shim.exe
59d9da9f9714003b915bcafbe1b41f53b121dde206ecc23984f62273e957766eece8d64ffc53011c328d3a2ad627aa0f4f7c39bbec8e7b64d0d2ee7b7e771423 *shim.exe

Binary file not shown.

View File

@@ -1 +1 @@
2.2.1
v3.1.2

View File

@@ -0,0 +1 @@
0116068768fc992fc536738396b33db3dafe6b0cf0e6f54f6d1aa8b0331f3cec *shim.exe

View File

@@ -0,0 +1 @@
d734c528e9f20581ed3c7aa71a458f7dff7e2780fa0c319ccb9c813cd8dbf656bd7e550b81d2aa3ee8775bff9a4e507bc0b25f075697405adca0f47d37835848 *shim.exe

Binary file not shown.

View File

@@ -0,0 +1 @@
1.1.0

View File

@@ -1,4 +1,4 @@
b624949df8b0e3a6153fdfb730a7c6f4990b6592ee0d922e1788433d276610f3 *Newtonsoft.Json.dll
9abb57d73d82a2d77008321a85aff2b62e5ac68bebb54ece8668c96cc112e36b *Newtonsoft.Json.Schema.dll
0318c8221ce4d44806f8def619bcc02886be0902aab80080e6251c50c6ca53a9 *Scoop.Validator.dll
40a70bee96d108701f8f2e81392f9b79fd003f1cb4e1653ad2429753153fd7ee *validator.exe
e1e27af7b07eeedf5ce71a9255f0422816a6fc5849a483c6714e1b472044fa9d *Newtonsoft.Json.dll
7496d5349a123a6e3696085662b2ff17b156ccdb0e30e0c396ac72d2da36ce1c *Newtonsoft.Json.Schema.dll
83b1006443e8c340ca4c631614fc2ce0d5cb9a28c851e3b59724299f58b1397f *Scoop.Validator.dll
87f8f8db2202a3fbef6f431d0b7e20cec9d32095c441927402041f3c4076c1b6 *validator.exe

View File

@@ -1,4 +1,4 @@
2fdf035661f349206f58ea1feed8805b7f9517a21f9c113e7301c69de160f184c774350a12a710046e3ff6baa37345d319b6f47fd24fbba4e042d54014bee511 *Newtonsoft.Json.dll
855ab2e30c9d523c9f321ae861c5969244185f660fa47e05cec96df8e2970d19843dbd3d89a0fca845544641915d1adf4b4a2145ef568dd99da7791e5064d70e *Newtonsoft.Json.Schema.dll
338793e6127330c0b05728291fcf18441127ffb56e1bd5c0f0588cd7436605f4b852f4bb622f655896a7eb7b1262add142b200fd5f37391b47d1401becb6b81c *Scoop.Validator.dll
d497c27b48f44f4cff270d3c8801b0cecc74108f8786a4a7c40e57541308ae33a69f5456cfc43ae1ce4214038d20da9fbeac1bcf76cc58d972863b58dab18401 *validator.exe
56eb7f070929b239642dab729537dde2c2287bdb852ad9e80b5358c74b14bc2b2dded910d0e3b6304ea27eb587e5f19db0a92e1cbae6a70fb20b4ef05057e4ac *Newtonsoft.Json.dll
78b12beb1e67ac4f6efa0fcba57b4b34ea6a31d8b369934d6b6a6617386ef9939ea453ac262916e5857ce0359eb809424ea33c676a87a8fdfd77a59b2ce96db0 *Newtonsoft.Json.Schema.dll
e9da4370aee4df47eedcf15d9749712eee513e5a9115b808617ddfcfde5bc47a0410edfb57508fcf51033c0be967611b2fd2c2ba944de7290c020cc67f77ac57 *Scoop.Validator.dll
58a0c37e98cac17822c7756bf6686a5fb74e711b8d986d13bd2f689f6b3b1f485fcd908d92cbc6a162a0e5974c2c5a43de57d15f1996be0aa405e41ec2ec8393 *validator.exe

View File

@@ -4,7 +4,7 @@ Push-Location $PSScriptRoot
. "$PSScriptRoot\..\..\lib\install.ps1"
if (!$Fast) {
Write-Host "Install dependencies ..."
Write-Host 'Install dependencies ...'
& "$PSScriptRoot\install.ps1"
}

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net45" />
<package id="Newtonsoft.Json.Schema" version="3.0.15-beta2" targetFramework="net45" />
<package id="Microsoft.Net.Compilers.Toolset" version="4.2.0" targetFramework="net45" developmentDependency="true" />
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net45" />
<package id="Newtonsoft.Json.Schema" version="4.0.1" targetFramework="net45" />
<package id="Microsoft.Net.Compilers.Toolset" version="4.10.0" targetFramework="net45"
developmentDependency="true" />
</packages>

View File

@@ -1,7 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="packages\Microsoft.Net.Compilers.Toolset.4.2.0\build\Microsoft.Net.Compilers.Toolset.props" Condition="Exists('packages\Microsoft.Net.Compilers.Toolset.4.2.0\build\Microsoft.Net.Compilers.Toolset.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Import
Project="packages\Microsoft.Net.Compilers.Toolset.4.10.0\build\Microsoft.Net.Compilers.Toolset.props"
Condition="Exists('packages\Microsoft.Net.Compilers.Toolset.4.10.0\build\Microsoft.Net.Compilers.Toolset.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props"
Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
@@ -14,12 +17,14 @@
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
<HintPath>packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Reference
Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
<HintPath>packages\Newtonsoft.Json.13.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.15-beta2\lib\net45\Newtonsoft.Json.Schema.dll</HintPath>
<Reference
Include="Newtonsoft.Json.Schema, Version=4.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
<HintPath>packages\Newtonsoft.Json.Schema.4.0.1\lib\net45\Newtonsoft.Json.Schema.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
@@ -41,8 +46,12 @@
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}.</ErrorText>
<ErrorText>This project references NuGet package(s) that are missing on this computer.
Enable NuGet Package Restore to download them. For more information, see
http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('packages\Microsoft.Net.Compilers.Toolset.4.2.0\build\Microsoft.Net.Compilers.Toolset.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.Net.Compilers.Toolset.4.2.0\build\Microsoft.Net.Compilers.Toolset.props'))" />
<Error
Condition="!Exists('packages\Microsoft.Net.Compilers.Toolset.4.10.0\build\Microsoft.Net.Compilers.Toolset.props')"
Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.Net.Compilers.Toolset.4.10.0\build\Microsoft.Net.Compilers.Toolset.props'))" />
</Target>
</Project>

View File

@@ -7,8 +7,7 @@ BeforeDiscovery {
'[\\/]\.git[\\/]',
'\.sublime-workspace$',
'\.DS_Store$',
'supporting(\\|/)validator(\\|/)packages(\\|/)*',
'supporting(\\|/)shimexe(\\|/)packages(\\|/)*'
'supporting(\\|/)validator(\\|/)packages(\\|/)*'
)
$repo_files = (Get-ChildItem $TestPath -File -Recurse).FullName |
Where-Object { $_ -inotmatch $($project_file_exclusions -join '|') }

View File

@@ -1,45 +0,0 @@
BeforeAll {
. "$PSScriptRoot\Scoop-TestLib.ps1"
. "$PSScriptRoot\..\lib\core.ps1"
. "$PSScriptRoot\..\lib\help.ps1"
. "$PSScriptRoot\..\libexec\scoop-alias.ps1" | Out-Null
}
Describe 'Manipulate Alias' -Tag 'Scoop' {
BeforeAll {
Mock shimdir { "$TestDrive\shims" }
Mock set_config { }
Mock get_config { @{} }
$shimdir = shimdir
ensure $shimdir
}
It 'Creates a new alias if alias doesn''t exist' {
$alias_file = "$shimdir\scoop-rm.ps1"
$alias_file | Should -Not -Exist
add_alias 'rm' '"hello, world!"'
& $alias_file | Should -Be 'hello, world!'
}
It 'Does not change existing alias if alias exists' {
$alias_file = "$shimdir\scoop-rm.ps1"
New-Item $alias_file -Type File -Force
$alias_file | Should -Exist
add_alias 'rm' 'test'
& $alias_file | Should -Not -Be 'test'
}
It 'Removes an existing alias' {
$alias_file = "$shimdir\scoop-rm.ps1"
add_alias 'rm' '"hello, world!"'
$alias_file | Should -Exist
Mock get_config { @(@{'rm' = 'scoop-rm' }) }
rm_alias 'rm'
$alias_file | Should -Not -Exist
}
}

View File

@@ -0,0 +1,45 @@
BeforeAll {
. "$PSScriptRoot\Scoop-TestLib.ps1"
. "$PSScriptRoot\..\lib\core.ps1"
. "$PSScriptRoot\..\lib\commands.ps1"
}
Describe 'Manipulate Alias' -Tag 'Scoop' {
BeforeAll {
Mock shimdir { "$TestDrive\shims" }
Mock set_config {}
Mock get_config { @{} }
$shimdir = shimdir
ensure $shimdir
}
It 'Creates a new alias if it does not exist' {
$alias_script = "$shimdir\scoop-rm.ps1"
$alias_script | Should -Not -Exist
add_alias 'rm' '"hello, world!"'
& $alias_script | Should -Be 'hello, world!'
}
It 'Skips an existing alias' {
$alias_script = "$shimdir\scoop-rm.ps1"
Mock abort {}
New-Item $alias_script -Type File -Force
$alias_script | Should -Exist
add_alias 'rm' '"test"'
Should -Invoke -CommandName abort -Times 1 -ParameterFilter { $msg -eq "File 'scoop-rm.ps1' already exists in shims directory." }
}
It 'Removes an existing alias' {
$alias_script = "$shimdir\scoop-rm.ps1"
$alias_script | Should -Exist
Mock get_config { @(@{'rm' = 'scoop-rm' }) }
Mock info {}
rm_alias 'rm'
$alias_script | Should -Not -Exist
Should -Invoke -CommandName info -Times 1 -ParameterFilter { $msg -eq "Removing alias 'rm'..." }
}
}

View File

@@ -1,6 +1,7 @@
BeforeAll {
. "$PSScriptRoot\Scoop-TestLib.ps1"
. "$PSScriptRoot\..\lib\core.ps1"
. "$PSScriptRoot\..\lib\system.ps1"
. "$PSScriptRoot\..\lib\install.ps1"
}
@@ -72,28 +73,6 @@ Describe 'Test-HelperInstalled' -Tag 'Scoop' {
}
}
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' {
Mock Test-HelperInstalled { $false }
Mock get_config { $false }
Test-Aria2Enabled | Should -BeFalse
Mock Test-HelperInstalled { $false }
Mock get_config { $true }
Test-Aria2Enabled | Should -BeFalse
Mock Test-HelperInstalled { $true }
Mock get_config { $false }
Test-Aria2Enabled | Should -BeFalse
}
}
Describe 'Test-CommandAvailable' -Tag 'Scoop' {
It 'should return true if command exists' {
Test-CommandAvailable 'Write-Host' | Should -BeTrue
@@ -167,7 +146,7 @@ Describe 'shim' -Tag 'Scoop', 'Windows' {
BeforeAll {
$working_dir = setup_working 'shim'
$shimdir = shimdir
$(ensure_in_path $shimdir) | Out-Null
Add-Path $shimdir
}
It "links a file onto the user's path" {
@@ -201,7 +180,7 @@ Describe 'rm_shim' -Tag 'Scoop', 'Windows' {
BeforeAll {
$working_dir = setup_working 'shim'
$shimdir = shimdir
$(ensure_in_path $shimdir) | Out-Null
Add-Path $shimdir
}
It 'removes shim from path' {
@@ -220,7 +199,7 @@ Describe 'get_app_name_from_shim' -Tag 'Scoop', 'Windows' {
BeforeAll {
$working_dir = setup_working 'shim'
$shimdir = shimdir
$(ensure_in_path $shimdir) | Out-Null
Add-Path $shimdir
Mock appsdir { $working_dir }
}
@@ -258,33 +237,20 @@ Describe 'get_app_name_from_shim' -Tag 'Scoop', 'Windows' {
}
}
Describe 'ensure_robocopy_in_path' -Tag 'Scoop', 'Windows' {
BeforeAll {
$shimdir = shimdir $false
Mock versiondir { "$PSScriptRoot\.." }
Describe 'cache_path' -Tag 'Scoop' {
It 'returns the correct cache path for a given input' {
$url = 'https://example.com/git.zip'
$ret = cache_path 'git' '2.44.0' $url
$inputStream = [System.IO.MemoryStream]::new([System.Text.Encoding]::UTF8.GetBytes($url))
$sha = (Get-FileHash -Algorithm SHA256 -InputStream $inputStream).Hash.ToLower().Substring(0, 7)
$ret | Should -Be "$cachedir\git#2.44.0#$sha.zip"
}
It 'shims robocopy when not on path' {
Mock Test-CommandAvailable { $false }
Test-CommandAvailable robocopy | Should -Be $false
ensure_robocopy_in_path
# "$shimdir/robocopy.ps1" | should -exist
"$shimdir/robocopy.exe" | Should -Exist
# clean up
rm_shim robocopy $(shimdir $false) | Out-Null
}
It 'does not shim robocopy when it is in path' {
Mock Test-CommandAvailable { $true }
Test-CommandAvailable robocopy | Should -Be $true
ensure_robocopy_in_path
# "$shimdir/robocopy.ps1" | should -not -exist
"$shimdir/robocopy.exe" | Should -Not -Exist
# # NOTE: Remove this 6 months after the feature ships.
It 'returns the old format cache path for a given input' {
Mock Test-Path { $true }
$ret = cache_path 'git' '2.44.0' 'https://example.com/git.zip'
$ret | Should -Be "$cachedir\git#2.44.0#https_example.com_git.zip"
}
}

View File

@@ -14,7 +14,7 @@ Describe 'Decompression function' -Tag 'Scoop', 'Windows', 'Decompress' {
function test_extract($extract_fn, $from, $removal) {
$to = (strip_ext $from) -replace '\.tar$', ''
& $extract_fn ($from -replace '/', '\') ($to -replace '/', '\') -Removal:$removal
& $extract_fn ($from -replace '/', '\') ($to -replace '/', '\') -Removal:$removal -ExtractDir $args[0]
return $to
}
@@ -25,7 +25,7 @@ Describe 'Decompression function' -Tag 'Scoop', 'Windows', 'Decompress' {
}
It 'Test cases should exist and hash should match' {
$testcases | Should -Exist
(Get-FileHash -Path $testcases -Algorithm SHA256).Hash.ToLower() | Should -Be '791bfce192917a2ff225dcdd87d23ae5f720b20178d85e68e4b1b56139cf8e6a'
(Get-FileHash -Path $testcases -Algorithm SHA256).Hash.ToLower() | Should -Be '591072faabd419b77932b7023e5899b4e05c0bf8e6859ad367398e6bfe1eb203'
}
It 'Test cases should be extracted correctly' {
{ Microsoft.PowerShell.Archive\Expand-Archive -Path $testcases -DestinationPath $working_dir } | Should -Not -Throw
@@ -50,15 +50,42 @@ Describe 'Decompression function' -Tag 'Scoop', 'Windows', 'Decompress' {
$test6_1 = "$working_dir\7ZipTest6.part01.rar"
$test6_2 = "$working_dir\7ZipTest6.part02.rar"
$test6_3 = "$working_dir\7ZipTest6.part03.rar"
$test7 = "$working_dir\NSISTest.exe"
}
AfterEach {
Remove-Item -Path $to -Recurse -Force
}
It 'extract normal compressed file' {
$to = test_extract 'Expand-7zipArchive' $test1
$to | Should -Exist
"$to\empty" | Should -Exist
(Get-ChildItem $to).Count | Should -Be 4
}
It 'extract "extract_dir" correctly' {
$to = test_extract 'Expand-7zipArchive' $test1 $false 'tmp'
$to | Should -Exist
"$to\empty" | Should -Exist
(Get-ChildItem $to).Count | Should -Be 1
}
It 'extract "extract_dir" with spaces correctly' {
$to = test_extract 'Expand-7zipArchive' $test1 $false 'tmp 2'
$to | Should -Exist
"$to\empty" | Should -Exist
(Get-ChildItem $to).Count | Should -Be 1
}
It 'extract "extract_dir" with nested folder with same name' {
$to = test_extract 'Expand-7zipArchive' $test1 $false 'keep\sub'
$to | Should -Exist
"$to\keep\empty" | Should -Exist
(Get-ChildItem $to).Count | Should -Be 1
(Get-ChildItem "$to\keep").Count | Should -Be 1
}
It 'extract nested compressed file' {
# file ext: tgz
$to = test_extract 'Expand-7zipArchive' $test2
@@ -94,62 +121,45 @@ Describe 'Decompression function' -Tag 'Scoop', 'Windows', 'Decompress' {
(Get-ChildItem $to).Count | Should -Be 1
}
It 'extract NSIS installer' {
$to = test_extract 'Expand-7zipArchive' $test7
$to | Should -Exist
"$to\empty" | Should -Exist
(Get-ChildItem $to).Count | Should -Be 1
}
It 'self-extract NSIS installer' {
$to = "$working_dir\NSIS Test"
$null = Invoke-ExternalCommand -FilePath $test7 -ArgumentList @('/S', '/NCRC', "/D=$to")
$to | Should -Exist
"$to\empty" | Should -Exist
(Get-ChildItem $to).Count | Should -Be 1
}
It 'works with "-Removal" switch ($removal param)' {
$test1 | Should -Exist
test_extract 'Expand-7zipArchive' $test1 $true
$to = test_extract 'Expand-7zipArchive' $test1 $true
$to | Should -Exist
$test1 | Should -Not -Exist
$test5_1 | Should -Exist
$test5_2 | Should -Exist
$test5_3 | Should -Exist
test_extract 'Expand-7zipArchive' $test5_1 $true
$to = test_extract 'Expand-7zipArchive' $test5_1 $true
$to | Should -Exist
$test5_1 | Should -Not -Exist
$test5_2 | Should -Not -Exist
$test5_3 | Should -Not -Exist
$test6_1 | Should -Exist
$test6_2 | Should -Exist
$test6_3 | Should -Exist
test_extract 'Expand-7zipArchive' $test6_1 $true
$to = test_extract 'Expand-7zipArchive' $test6_1 $true
$to | Should -Exist
$test6_1 | Should -Not -Exist
$test6_2 | Should -Not -Exist
$test6_3 | Should -Not -Exist
}
}
Context 'zstd extraction' {
BeforeAll {
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' {
$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' {
$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)' {
$test1 | Should -Exist
test_extract 'Expand-ZstdArchive' $test1 $true
$test1 | Should -Not -Exist
}
}
Context 'msi extraction' {
BeforeAll {
@@ -158,11 +168,13 @@ Describe 'Decompression function' -Tag 'Scoop', 'Windows', 'Decompress' {
} elseif (!(installed lessmsi)) {
scoop install lessmsi
}
Copy-Item "$working_dir\MSITest.msi" "$working_dir\MSI Test.msi"
$test1 = "$working_dir\MSITest.msi"
$test2 = "$working_dir\MSITestNull.msi"
$test2 = "$working_dir\MSI Test.msi"
$test3 = "$working_dir\MSITestNull.msi"
}
It 'extract normal MSI file' {
It 'extract normal MSI file using msiexec' {
Mock get_config { $false }
$to = test_extract 'Expand-MsiArchive' $test1
$to | Should -Exist
@@ -170,12 +182,32 @@ Describe 'Decompression function' -Tag 'Scoop', 'Windows', 'Decompress' {
(Get-ChildItem "$to\MSITest").Count | Should -Be 1
}
It 'extract empty MSI file using lessmsi' {
It 'extract normal MSI file with whitespace in path using msiexec' {
Mock get_config { $false }
$to = test_extract 'Expand-MsiArchive' $test2
$to | Should -Exist
"$to\MSITest\empty" | Should -Exist
(Get-ChildItem "$to\MSITest").Count | Should -Be 1
}
It 'extract normal MSI file using lessmsi' {
Mock get_config { $true }
$to = test_extract 'Expand-MsiArchive' $test1
$to | Should -Exist
}
It 'extract normal MSI file with whitespace in path using lessmsi' {
Mock get_config { $true }
$to = test_extract 'Expand-MsiArchive' $test2
$to | Should -Exist
}
It 'extract empty MSI file using lessmsi' {
Mock get_config { $true }
$to = test_extract 'Expand-MsiArchive' $test3
$to | Should -Exist
}
It 'works with "-Removal" switch ($removal param)' {
Mock get_config { $false }
$test1 | Should -Exist

View File

@@ -14,11 +14,6 @@ Describe 'Package Dependencies' -Tag 'Scoop' {
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
@@ -27,7 +22,6 @@ Describe 'Package Dependencies' -Tag 'Scoop' {
}
It 'Allow $Uri be $null' {
Test-7zipRequirement -Uri $null | Should -BeFalse
Test-ZstdRequirement -Uri $null | Should -BeFalse
Test-LessmsiRequirement -Uri $null | Should -BeFalse
}
}
@@ -71,6 +65,7 @@ Describe 'Package Dependencies' -Tag 'Scoop' {
BeforeAll {
Mock Test-HelperInstalled { $false }
Mock get_config { $true } -ParameterFilter { $name -eq 'USE_LESSMSI' }
Mock get_config { $false } -ParameterFilter { $name -eq 'USE_EXTERNAL_7ZIP' }
Mock Get-Manifest { 'lessmsi', @{}, $null, $null } -ParameterFilter { $app -eq 'lessmsi' }
Mock Get-Manifest { '7zip', @{ url = 'test.msi' }, $null, $null } -ParameterFilter { $app -eq '7zip' }
Mock Get-Manifest { 'innounp', @{}, $null, $null } -ParameterFilter { $app -eq 'innounp' }

View File

@@ -0,0 +1,49 @@
BeforeAll {
. "$PSScriptRoot\Scoop-TestLib.ps1"
. "$PSScriptRoot\..\lib\core.ps1"
. "$PSScriptRoot\..\lib\download.ps1"
}
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' {
Mock Test-HelperInstalled { $false }
Mock get_config { $false }
Test-Aria2Enabled | Should -BeFalse
Mock Test-HelperInstalled { $false }
Mock get_config { $true }
Test-Aria2Enabled | Should -BeFalse
Mock Test-HelperInstalled { $true }
Mock get_config { $false }
Test-Aria2Enabled | Should -BeFalse
}
}
Describe 'url_filename' -Tag 'Scoop' {
It 'should extract the real filename from an url' {
url_filename 'http://example.org/foo.txt' | Should -Be 'foo.txt'
url_filename 'http://example.org/foo.txt?var=123' | Should -Be 'foo.txt'
}
It 'can be tricked with a hash to override the real filename' {
url_filename 'http://example.org/foo-v2.zip#/foo.zip' | Should -Be 'foo.zip'
}
}
Describe 'url_remote_filename' -Tag 'Scoop' {
It 'should extract the real filename from an url' {
url_remote_filename 'http://example.org/foo.txt' | Should -Be 'foo.txt'
url_remote_filename 'http://example.org/foo.txt?var=123' | Should -Be 'foo.txt'
}
It 'can not be tricked with a hash to override the real filename' {
url_remote_filename 'http://example.org/foo-v2.zip#/foo.zip' | Should -Be 'foo-v2.zip'
}
}

View File

@@ -17,6 +17,12 @@ Describe 'getopt' -Tag 'Scoop' {
$err | Should -Be 'Option --arb requires an argument.'
}
It 'handle space in quote' {
$opt, $rem, $err = getopt '-x', 'space arg' 'x:' ''
$err | Should -BeNullOrEmpty
$opt.x | Should -Be 'space arg'
}
It 'handle unrecognized short option' {
$null, $null, $err = getopt '-az' 'a' ''
$err | Should -Be 'Option -z not recognized.'

View File

@@ -1,6 +1,7 @@
BeforeAll {
. "$PSScriptRoot\Scoop-TestLib.ps1"
. "$PSScriptRoot\..\lib\core.ps1"
. "$PSScriptRoot\..\lib\system.ps1"
. "$PSScriptRoot\..\lib\manifest.ps1"
. "$PSScriptRoot\..\lib\install.ps1"
}
@@ -11,34 +12,10 @@ Describe 'appname_from_url' -Tag 'Scoop' {
}
}
Describe 'url_filename' -Tag 'Scoop' {
It 'should extract the real filename from an url' {
url_filename 'http://example.org/foo.txt' | Should -Be 'foo.txt'
url_filename 'http://example.org/foo.txt?var=123' | Should -Be 'foo.txt'
}
It 'can be tricked with a hash to override the real filename' {
url_filename 'http://example.org/foo-v2.zip#/foo.zip' | Should -Be 'foo.zip'
}
}
Describe 'url_remote_filename' -Tag 'Scoop' {
It 'should extract the real filename from an url' {
url_remote_filename 'http://example.org/foo.txt' | Should -Be 'foo.txt'
url_remote_filename 'http://example.org/foo.txt?var=123' | Should -Be 'foo.txt'
}
It 'can not be tricked with a hash to override the real filename' {
url_remote_filename 'http://example.org/foo-v2.zip#/foo.zip' | Should -Be 'foo-v2.zip'
}
}
Describe 'is_in_dir' -Tag 'Scoop', 'Windows' {
It 'should work correctly' {
is_in_dir 'C:\test' 'C:\foo' | Should -BeFalse
is_in_dir 'C:\test' 'C:\test\foo\baz.zip' | Should -BeTrue
is_in_dir 'test' "$PSScriptRoot" | Should -BeTrue
is_in_dir "$PSScriptRoot\..\" "$PSScriptRoot" | Should -BeFalse
}
}
@@ -47,27 +24,28 @@ Describe 'env add and remove path' -Tag 'Scoop', 'Windows' {
BeforeAll {
# test data
$manifest = @{
'env_add_path' = @('foo', 'bar')
'env_add_path' = @('foo', 'bar', '.', '..')
}
$testdir = Join-Path $PSScriptRoot 'path-test-directory'
$global = $false
# store the original path to prevent leakage of tests
$origPath = $env:PATH
}
It 'should concat the correct path' {
Mock add_first_in_path {}
Mock remove_from_path {}
Mock Add-Path {}
Mock Remove-Path {}
# adding
env_add_path $manifest $testdir $global
Assert-MockCalled add_first_in_path -Times 1 -ParameterFilter { $dir -like "$testdir\foo" }
Assert-MockCalled add_first_in_path -Times 1 -ParameterFilter { $dir -like "$testdir\bar" }
Should -Invoke -CommandName Add-Path -Times 1 -ParameterFilter { $Path -like "$testdir\foo" }
Should -Invoke -CommandName Add-Path -Times 1 -ParameterFilter { $Path -like "$testdir\bar" }
Should -Invoke -CommandName Add-Path -Times 1 -ParameterFilter { $Path -like $testdir }
Should -Invoke -CommandName Add-Path -Times 0 -ParameterFilter { $Path -like $PSScriptRoot }
env_rm_path $manifest $testdir $global
Assert-MockCalled remove_from_path -Times 1 -ParameterFilter { $dir -like "$testdir\foo" }
Assert-MockCalled remove_from_path -Times 1 -ParameterFilter { $dir -like "$testdir\bar" }
Should -Invoke -CommandName Remove-Path -Times 1 -ParameterFilter { $Path -like "$testdir\foo" }
Should -Invoke -CommandName Remove-Path -Times 1 -ParameterFilter { $Path -like "$testdir\bar" }
Should -Invoke -CommandName Remove-Path -Times 1 -ParameterFilter { $Path -like $testdir }
Should -Invoke -CommandName Remove-Path -Times 0 -ParameterFilter { $Path -like $PSScriptRoot }
}
}

View File

@@ -1,13 +1,13 @@
# copies fixtures to a working directory
function setup_working($name) {
$fixtures = "$PSScriptRoot/fixtures/$name"
$fixtures = "$PSScriptRoot\fixtures\$name"
if (!(Test-Path $fixtures)) {
Write-Host "couldn't find fixtures for $name at $fixtures" -f red
exit 1
}
# reset working dir
$working_dir = "$([IO.Path]::GetTempPath())ScoopTestFixtures/$name"
$working_dir = "$([IO.Path]::GetTempPath())ScoopTestFixtures\$name"
if (Test-Path $working_dir) {
Remove-Item -Recurse -Force $working_dir

View File

@@ -99,7 +99,7 @@ Describe 'versions comparison' -Tag 'Scoop' {
Compare-Version 'nightly-20190801' 'nightly-20200801' | Should -Be 0
}
It 'handles nightly versions with `update_nightly`' {
It "handles nightly versions with 'update_nightly'" {
function get_config { $true }
Mock Get-Date { '20200801' }
Compare-Version 'nightly-20200801' 'nightly' | Should -Be 0

View File

@@ -70,19 +70,6 @@ if ($env:CI -eq $true) {
Invoke-WebRequest -Uri $source -OutFile $destination
& 7z.exe x "$env:SCOOP_HELPERS_PATH\innounp.rar" -o"$env:SCOOP_HELPERS_PATH\innounp" -y | Out-Null
}
# Only download zstd for AppVeyor, GitHub Actions has zstd installed by default
if ($env:BHBuildSystem -eq 'AppVeyor') {
$env:SCOOP_ZSTD_PATH = "$env:SCOOP_HELPERS_PATH\zstd\zstd.exe"
if (!(Test-Path $env:SCOOP_ZSTD_PATH)) {
$source = 'https://github.com/facebook/zstd/releases/download/v1.5.1/zstd-v1.5.1-win32.zip'
$destination = "$env:SCOOP_HELPERS_PATH\zstd.zip"
Invoke-WebRequest -Uri $source -OutFile $destination
& 7z.exe x "$env:SCOOP_HELPERS_PATH\zstd.zip" -o"$env:SCOOP_HELPERS_PATH\zstd" -y | Out-Null
}
} else {
$env:SCOOP_ZSTD_PATH = (Get-Command zstd.exe).Path
}
}
}

Binary file not shown.