diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index d142debcb2..2e42f0d94d 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -7,6 +7,7 @@ Failing to do so will most likely result in closing of this PR without any explanation. It is also mandatory to open a relevant issue for discussion with the maintainers, before creating any new PR. + Read the contributing guide first to save both your and our time. --> #### Description @@ -27,5 +28,6 @@ Relates to #XXXX #### Checklist: +- [ ] I have read the [Contributing Guide](https://github.com/ScoopInstaller/.github/blob/main/.github/CONTRIBUTING.md). - [ ] I have updated the documentation accordingly. - [ ] I have updated the tests accordingly. diff --git a/README.md b/README.md index 29d1eda03e..109396074b 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,22 @@ $env:SCOOP_GLOBAL='F:\GlobalScoopApps' # run the installer ``` +### Configure Scoop to store downloads to a Custom Directory by changing `SCOOP_CACHE` + +```powershell +$env:SCOOP_CACHE='F:\ScoopCache' +[Environment]::SetEnvironmentVariable('SCOOP_CACHE', $env:SCOOP_CACHE, 'Machine') +# run the installer +``` + +### Configure Scoop to use a GitHub API token during searching and checkver by setting `SCOOP_CHECKVER_TOKEN` + +```powershell +$env:SCOOP_CHECKVER_TOKEN='' +[Environment]::SetEnvironmentVariable('SCOOP_CHECKVER_TOKEN', $env:SCOOP_CHECKVER_TOKEN, 'Machine') +# search for an app +``` + ## [Documentation](https://github.com/ScoopInstaller/Scoop/wiki) ## Multi-connection downloads with `aria2` @@ -115,6 +131,7 @@ You can tweak the following `aria2` settings with the `scoop config` command: - [aria2-split](https://aria2.github.io/manual/en/html/aria2c.html#cmdoption-s) (default: 5) - [aria2-max-connection-per-server](https://aria2.github.io/manual/en/html/aria2c.html#cmdoption-x) (default: 5) - [aria2-min-split-size](https://aria2.github.io/manual/en/html/aria2c.html#cmdoption-k) (default: 5M) +- [aria2-options](https://aria2.github.io/manual/en/html/aria2c.html#options) (default: ) ## Inspiration @@ -129,6 +146,10 @@ Since installers are common, Scoop supports them too (and their uninstallers). Scoop is also great at handling single-file programs and Powershell scripts. These don't even need to be compressed. See the [runat](https://github.com/ScoopInstaller/Main/blob/master/bucket/runat.json) package for an example: it's really just a GitHub gist. +### Contribute to this project + +If you'd like to improve Scoop by adding features or fixing bugs, please read our [Contributing Guide](https://github.com/ScoopInstaller/.github/blob/main/.github/CONTRIBUTING.md). + ### Support this project If you find Scoop useful and would like to support ongoing development and maintenance, here's how: @@ -145,8 +166,6 @@ The following buckets are known to scoop: - [nerd-fonts](https://github.com/matthewjberger/scoop-nerd-fonts) - Nerd Fonts - [nirsoft](https://github.com/kodybrown/scoop-nirsoft) - Almost all of the [250+](https://rasa.github.io/scoop-directory/by-apps#kodybrown_scoop-nirsoft) apps from [Nirsoft](https://nirsoft.net) - [java](https://github.com/ScoopInstaller/Java) - A collection of Java development kits (JDKs), Java runtime engines (JREs), Java's virtual machine debugging tools and Java based runtime engines. -- [jetbrains](https://github.com/Ash258/Scoop-JetBrains) - Installers for all JetBrains utilities and IDEs - - [nonportable](https://github.com/TheRandomLabs/scoop-nonportable) - Non-portable apps (may require UAC) - [php](https://github.com/ScoopInstaller/PHP) - Installers for most versions of PHP - [versions](https://github.com/ScoopInstaller/Versions) - Alternative versions of apps found in other buckets @@ -162,4 +181,4 @@ scoop bucket add extras ## Other application buckets -Many other application buckets hosted on Github can be found in the [Scoop Directory](https://github.com/rasa/scoop-directory). +Many other application buckets hosted on Github can be found in the [Scoop Directory](https://rasa.github.io/scoop-directory/) or via [other search engines](https://rasa.github.io/scoop-directory/#other-search-engines). diff --git a/bin/auto-pr.ps1 b/bin/auto-pr.ps1 index 388c5099f6..a78a4153fc 100644 --- a/bin/auto-pr.ps1 +++ b/bin/auto-pr.ps1 @@ -2,19 +2,21 @@ .SYNOPSIS Updates manifests and pushes them or creates pull-requests. .DESCRIPTION - Updates manifests and pushes them directly to the master branch or creates pull-requests for upstream. + Updates manifests and pushes them directly to the origin branch or creates pull-requests for upstream. .PARAMETER Upstream Upstream repository with the target branch. Must be in format '/:' +.PARAMETER OriginBranch + Origin (local) branch name. .PARAMETER App Manifest name to search. Placeholders are supported. .PARAMETER Dir The directory where to search for manifests. .PARAMETER Push - Push updates directly to 'origin master'. + Push updates directly to 'origin branch'. .PARAMETER Request - Create pull-requests on 'upstream master' for each update. + Create pull-requests on 'upstream branch' for each update. .PARAMETER Help Print help to console. .PARAMETER SpecialSnowflakes @@ -37,6 +39,7 @@ param( $true })] [String] $Upstream, + [String] $OriginBranch = 'master', [String] $App = '*', [Parameter(Mandatory = $true)] [ValidateScript( { @@ -65,12 +68,12 @@ if ((!$Push -and !$Request) -or $Help) { Usage: auto-pr.ps1 [OPTION] Mandatory options: - -p, -push push updates directly to 'origin master' - -r, -request create pull-requests on 'upstream master' for each update + -p, -push push updates directly to 'origin branch' + -r, -request create pull-requests on 'upstream branch' for each update Optional options: -u, -upstream upstream repository with target branch - only used if -r is set (default: lukesampson/scoop:master) + -o, -originbranch origin (local) branch name -h, -help '@ exit 0 @@ -104,7 +107,7 @@ function pull_requests($json, [String] $app, [String] $upstream, [String] $manif $homepage = $json.homepage $branch = "manifest/$app-$version" - execute 'hub checkout master' + execute "hub checkout $OriginBranch" Write-Host "hub rev-parse --verify $branch" -ForegroundColor Green hub rev-parse --verify $branch @@ -141,7 +144,7 @@ a new version of [$app]($homepage) is available. | New version | $version | "@ - hub pull-request -m "$msg" -b '$upstream' -h '$branch' + hub pull-request -m "$msg" -b "$upstream" -h "$branch" if ($LASTEXITCODE -gt 0) { execute 'hub reset' abort "Pull Request failed! (hub pull-request -m '${app}: Update to version $version' -b '$upstream' -h '$branch')" @@ -150,11 +153,11 @@ a new version of [$app]($homepage) is available. Write-Host 'Updating ...' -ForegroundColor DarkCyan if ($Push) { - execute 'hub pull origin master' - execute 'hub checkout master' + execute "hub pull origin $OriginBranch" + execute "hub checkout $OriginBranch" } else { - execute 'hub pull upstream master' - execute 'hub push origin master' + execute "hub pull upstream $OriginBranch" + execute "hub push origin $OriginBranch" } . "$PSScriptRoot\checkver.ps1" -App $App -Dir $Dir -Update -SkipUpdated:$SkipUpdated @@ -198,10 +201,10 @@ hub diff --name-only | ForEach-Object { if ($Push) { Write-Host 'Pushing updates ...' -ForegroundColor DarkCyan - execute 'hub push origin master' + execute "hub push origin $OriginBranch" } else { - Write-Host 'Returning to master branch and removing unstaged files ...' -ForegroundColor DarkCyan - execute 'hub checkout -f master' + Write-Host "Returning to $OriginBranch branch and removing unstaged files ..." -ForegroundColor DarkCyan + execute "hub checkout -f $OriginBranch" } execute 'hub reset --hard' diff --git a/bin/checkhashes.ps1 b/bin/checkhashes.ps1 index 102230494b..a6c93e6721 100644 --- a/bin/checkhashes.ps1 +++ b/bin/checkhashes.ps1 @@ -14,7 +14,7 @@ Manifests without mismatch will not be shown. .PARAMETER UseCache Downloaded files will not be deleted after script finish. - Should not be used, because check should be used for downloading actual version of file (as normal user, not finding in some document from vendors, which could be damaged / wrong (Example: Slack@3.3.1 lukesampson/scoop-extras#1192)), not some previously downloaded. + Should not be used, because check should be used for downloading actual version of file (as normal user, not finding in some document from vendors, which could be damaged / wrong (Example: Slack@3.3.1 ScoopInstaller/Extras#1192)), not some previously downloaded. .EXAMPLE PS BUCKETROOT> .\bin\checkhashes.ps1 Check all manifests for hash mismatch. diff --git a/bin/checkver.ps1 b/bin/checkver.ps1 index a83da1f64e..95df0262f4 100644 --- a/bin/checkver.ps1 +++ b/bin/checkver.ps1 @@ -224,7 +224,13 @@ while ($in_progress -gt 0) { } if ($jsonpath) { - $ver = json_path $page $jsonpath + # Return only a single value if regex is absent + $noregex = [String]::IsNullOrEmpty($regex) + # If reverse is ON and regex is ON, + # Then reverse would have no effect because regex handles reverse + # on its own + # So in this case we have to disable reverse + $ver = json_path $page $jsonpath $null ($reverse -and $noregex) $noregex if (!$ver) { $ver = json_path_legacy $page $jsonpath } diff --git a/buckets.json b/buckets.json index 8893bdd198..2157d22c7a 100644 --- a/buckets.json +++ b/buckets.json @@ -2,12 +2,10 @@ "main": "https://github.com/ScoopInstaller/Main", "extras": "https://github.com/ScoopInstaller/Extras", "versions": "https://github.com/ScoopInstaller/Versions", - "nightlies": "https://github.com/ScoopInstaller/Nightlies", "nirsoft": "https://github.com/kodybrown/scoop-nirsoft", "php": "https://github.com/ScoopInstaller/PHP", "nerd-fonts": "https://github.com/matthewjberger/scoop-nerd-fonts", "nonportable": "https://github.com/TheRandomLabs/scoop-nonportable", "java": "https://github.com/ScoopInstaller/Java", - "games": "https://github.com/Calinou/scoop-games", - "jetbrains": "https://github.com/Ash258/Scoop-JetBrains" + "games": "https://github.com/Calinou/scoop-games" } diff --git a/lib/core.ps1 b/lib/core.ps1 index c67970258f..fc154c5d33 100644 --- a/lib/core.ps1 +++ b/lib/core.ps1 @@ -434,7 +434,8 @@ function Invoke-ExternalCommand { return $false } if ($LogPath -and ($FilePath -notmatch '(^|\W)msiexec($|\W)')) { - Out-File -FilePath $LogPath -Encoding ASCII -Append -InputObject $Process.StandardOutput.ReadToEnd() + Out-File -FilePath $LogPath -Encoding Default -Append -InputObject $Process.StandardOutput.ReadToEnd() + Out-File -FilePath $LogPath -Encoding Default -Append -InputObject $Process.StandardError.ReadToEnd() } $Process.WaitForExit() if ($Process.ExitCode -ne 0) { @@ -530,72 +531,81 @@ function get_app_name($path) { return '' } -function get_app_name_from_ps1_shim($shim_ps1) { - if (!(Test-Path($shim_ps1))) { +function get_app_name_from_shim($shim) { + if (!(Test-Path($shim))) { return '' } - $content = (Get-Content $shim_ps1 -Encoding utf8) -join ' ' + $content = (Get-Content $shim -Encoding UTF8) -join ' ' return get_app_name $content } -function warn_on_overwrite($shim_ps1, $path) { - if (!(Test-Path($shim_ps1))) { +function warn_on_overwrite($shim, $path) { + if (!(Test-Path($shim))) { return } - $shim_app = get_app_name_from_ps1_shim $shim_ps1 + $shim_app = get_app_name_from_shim $shim $path_app = get_app_name $path if ($shim_app -eq $path_app) { return } - $filename = [System.IO.Path]::GetFileName($path) - warn "Overwriting shim to $filename installed from $shim_app" + $shimname = (fname $shim) -replace '\.shim$', '.exe' + $filename = (fname $path) -replace '\.shim$', '.exe' + warn "Overwriting shim ('$shimname' -> '$filename') installed from $shim_app" } function shim($path, $global, $name, $arg) { - if(!(test-path $path)) { abort "Can't shim '$(fname $path)': couldn't find '$path'." } + if (!(Test-Path $path)) { abort "Can't shim '$(fname $path)': couldn't find '$path'." } $abs_shimdir = ensure (shimdir $global) - if(!$name) { $name = strip_ext (fname $path) } + if (!$name) { $name = strip_ext (fname $path) } $shim = "$abs_shimdir\$($name.tolower())" - warn_on_overwrite "$shim.ps1" $path - # convert to relative path Push-Location $abs_shimdir - $relative_path = resolve-path -relative $path + $relative_path = Resolve-Path -Relative $path Pop-Location - $resolved_path = resolve-path $path - - # if $path points to another drive resolve-path prepends .\ which could break shims - if($relative_path -match "^(.\\[\w]:).*$") { - write-output "`$path = `"$path`"" | out-file "$shim.ps1" -encoding utf8 - } else { - # Setting PSScriptRoot in Shim if it is not defined, so the shim doesn't break in PowerShell 2.0 - Write-Output "if (!(Test-Path Variable:PSScriptRoot)) { `$PSScriptRoot = Split-Path `$MyInvocation.MyCommand.Path -Parent }" | Out-File "$shim.ps1" -Encoding utf8 - write-output "`$path = join-path `"`$psscriptroot`" `"$relative_path`"" | out-file "$shim.ps1" -Encoding utf8 -Append - } + $resolved_path = Resolve-Path $path - if($path -match '\.jar$') { - "if(`$myinvocation.expectingInput) { `$input | & java -jar `$path $arg @args } else { & java -jar `$path $arg @args }" | out-file "$shim.ps1" -encoding utf8 -append - } else { - "if(`$myinvocation.expectingInput) { `$input | & `$path $arg @args } else { & `$path $arg @args }" | out-file "$shim.ps1" -encoding utf8 -append - } - - if($path -match '\.(exe|com)$') { + if ($path -match '\.(exe|com)$') { # for programs with no awareness of any shell - Copy-Item (get_shim_path) "$shim.exe" -force - write-output "path = $resolved_path" | out-file "$shim.shim" -encoding utf8 - if($arg) { - write-output "args = $arg" | out-file "$shim.shim" -encoding utf8 -append + warn_on_overwrite "$shim.shim" $path + Copy-Item (get_shim_path) "$shim.exe" -Force + Write-Output "path = $resolved_path" | Out-File "$shim.shim" -Encoding ASCII + if ($arg) { + Write-Output "args = $arg" | Out-File "$shim.shim" -Encoding ASCII -Append } - } elseif($path -match '\.(bat|cmd)$') { + } elseif ($path -match '\.(bat|cmd)$') { # shim .bat, .cmd so they can be used by programs with no awareness of PSH - "@`"$resolved_path`" $arg %*" | out-file "$shim.cmd" -encoding ascii + warn_on_overwrite "$shim.cmd" $path + "@rem $resolved_path +@`"$resolved_path`" $arg %*" | Out-File "$shim.cmd" -Encoding ASCII + + warn_on_overwrite $shim $path + "#!/bin/sh +# $resolved_path +MSYS2_ARG_CONV_EXCL=/C cmd.exe /C `"$resolved_path`" $arg `"$@`"" | Out-File $shim -Encoding ASCII + } elseif ($path -match '\.ps1$') { + # if $path points to another drive resolve-path prepends .\ which could break shims + warn_on_overwrite "$shim.ps1" $path + $ps1text = if ($relative_path -match '^(.\\[\w]:).*$') { + "# $resolved_path +`$path = `"$path`" +if(`$myinvocation.expectingInput) { `$input | & `$path $arg @args } else { & `$path $arg @args } +exit `$lastexitcode" + } else { + # Setting PSScriptRoot in Shim if it is not defined, so the shim doesn't break in PowerShell 2.0 + "# $resolved_path +if (!(Test-Path Variable:PSScriptRoot)) { `$PSScriptRoot = Split-Path `$MyInvocation.MyCommand.Path -Parent } +`$path = join-path `"`$psscriptroot`" `"$relative_path`" +if(`$myinvocation.expectingInput) { `$input | & `$path $arg @args } else { & `$path $arg @args } +exit `$lastexitcode" + } + $ps1text | Out-File "$shim.ps1" -Encoding ASCII - "#!/bin/sh`nMSYS2_ARG_CONV_EXCL=/C cmd.exe /C `"$resolved_path`" $arg `"$@`"" | out-file $shim -encoding ascii - } elseif($path -match '\.ps1$') { # make ps1 accessible from cmd.exe - "@echo off + warn_on_overwrite "$shim.cmd" $path + "@rem $resolved_path +@echo off setlocal enabledelayedexpansion set args=%* :: replace problem characters in arguments @@ -604,21 +614,63 @@ set args=%args:(=``(% set args=%args:)=``)% set invalid=`"=' if !args! == !invalid! ( set args= ) -powershell -noprofile -ex unrestricted `"& '$resolved_path' $arg %args%;exit `$lastexitcode`"" | out-file "$shim.cmd" -encoding ascii +where /q pwsh.exe +if %errorlevel% equ 0 ( + pwsh -noprofile -ex unrestricted -command `"& '$resolved_path' $arg %args%;exit `$lastexitcode`" +) else ( + powershell -noprofile -ex unrestricted -command `"& '$resolved_path' $arg %args%;exit `$lastexitcode`" +)" | Out-File "$shim.cmd" -Encoding ASCII + + warn_on_overwrite $shim $path + "#!/bin/sh +# $resolved_path +if command -v pwsh.exe &> /dev/null; then + pwsh.exe -noprofile -ex unrestricted -command `"& '$resolved_path' $arg $@;exit \`$lastexitcode`" +else + powershell.exe -noprofile -ex unrestricted -command `"& '$resolved_path' $arg $@;exit \`$lastexitcode`" +fi" | Out-File $shim -Encoding ASCII + } elseif ($path -match '\.jar$') { + warn_on_overwrite "$shim.cmd" $path + "@rem $resolved_path +@java -jar `"$resolved_path`" $arg %*" | Out-File "$shim.cmd" -Encoding ASCII + + warn_on_overwrite $shim $path + "#!/bin/sh +# $resolved_path +java -jar `"$resolved_path`" $arg `"$@`"" | Out-File $shim -Encoding ASCII + } elseif ($path -match '\.py$') { + warn_on_overwrite "$shim.cmd" $path + "@rem $resolved_path +@python `"$resolved_path`" $arg %*" | Out-File "$shim.cmd" -Encoding ASCII + + warn_on_overwrite $shim $path + "#!/bin/sh +# $resolved_path +python `"$resolved_path`" $arg `"$@`"" | Out-File $shim -Encoding ASCII + } else { + warn_on_overwrite "$shim.cmd" $path + # find path to Git's bash so that batch scripts can run bash scripts + $gitdir = (Get-Item (Get-Command git -ErrorAction:Stop).Source -ErrorAction:Stop).Directory.Parent + if ($gitdir.FullName -imatch 'mingw') { + $gitdir = $gitdir.Parent + } + "@rem $resolved_path +@`"$(Join-Path (Join-Path $gitdir.FullName 'bin') 'bash.exe')`" `"$resolved_path`" $arg %*" | Out-File "$shim.cmd" -Encoding ASCII - "#!/bin/sh`npowershell.exe -noprofile -ex unrestricted `"$resolved_path`" $arg `"$@`"" | out-file $shim -encoding ascii - } elseif($path -match '\.jar$') { - "@java -jar `"$resolved_path`" $arg %*" | out-file "$shim.cmd" -encoding ascii - "#!/bin/sh`njava -jar `"$resolved_path`" $arg `"$@`"" | out-file $shim -encoding ascii + warn_on_overwrite $shim $path + "#!/bin/sh +# $resolved_path +`"$resolved_path`" $arg `"$@`"" | Out-File $shim -Encoding ASCII } } function get_shim_path() { - $shim_path = "$(versiondir 'scoop' 'current')\supporting\shimexe\bin\shim.exe" + $shim_path = "$(versiondir 'scoop' 'current')\supporting\shims\kiennq\shim.exe" $shim_version = get_config 'shim' 'default' switch ($shim_version) { '71' { $shim_path = "$(versiondir 'scoop' 'current')\supporting\shims\71\shim.exe"; Break } - 'kiennq' { $shim_path = "$(versiondir 'scoop' 'current')\supporting\shims\kiennq\shim.exe"; Break } + 'scoopcs' { $shim_path = "$(versiondir 'scoop' 'current')\supporting\shimexe\bin\shim.exe"; Break } + 'kiennq' { Break } # for backward compatibility 'default' { Break } default { warn "Unknown shim version: '$shim_version'" } } @@ -814,7 +866,7 @@ function applist($apps, $global) { } function parse_app([string] $app) { - if($app -match '(?:(?[a-zA-Z0-9-]+)\/)?(?.*.json$|[a-zA-Z0-9-_.]+)(?:@(?.*))?') { + if($app -match '(?:(?[a-zA-Z0-9-]+)\/)?(?.*\.json$|[a-zA-Z0-9-_.]+)(?:@(?.*))?') { return $matches['app'], $matches['bucket'], $matches['version'] } return $app, $null, $null diff --git a/lib/decompress.ps1 b/lib/decompress.ps1 index 505bfffef9..db077d4155 100644 --- a/lib/decompress.ps1 +++ b/lib/decompress.ps1 @@ -110,7 +110,7 @@ function Expand-7zipArchive { # Check for tar $Status = Invoke-ExternalCommand $7zPath @('l', "`"$Path`"") -LogPath $LogPath if ($Status) { - $TarFile = (Get-Content -Path $LogPath)[-4] -replace '.{53}(.*)', '$1' # get inner tar file name + $TarFile = (Get-Content -Path $LogPath)[-5] -replace '.{53}(.*)', '$1' # get inner tar file name Expand-7zipArchive -Path "$DestinationPath\$TarFile" -DestinationPath $DestinationPath -ExtractDir $ExtractDir -Removal } else { abort "Failed to list files in $Path.`nNot a 7-Zip supported archive file." diff --git a/lib/depends.ps1 b/lib/depends.ps1 index 206f7bd764..54585553da 100644 --- a/lib/depends.ps1 +++ b/lib/depends.ps1 @@ -89,7 +89,7 @@ function install_deps($manifest, $arch) { if (!(Test-HelperInstalled -Helper Innounp) -and $manifest.innosetup) { $deps += 'innounp' } - if (!(Test-HelperInstalled -Helper Zstd) -and (Test-ZstdRequirement -URL (url $manifest $arch))) { + if (!(Test-HelperInstalled -Helper Zstd) -and (Test-ZstdRequirement -URL (script:url $manifest $arch))) { $deps += 'zstd' } diff --git a/lib/diagnostic.ps1 b/lib/diagnostic.ps1 index 9eb7579e1d..784a673f98 100644 --- a/lib/diagnostic.ps1 +++ b/lib/diagnostic.ps1 @@ -39,6 +39,10 @@ function check_main_bucket { } function check_long_paths { + if ([System.Environment]::OSVersion.Version.Major -lt 10 -or [System.Environment]::OSVersion.Version.Build -lt 1607) { + warn 'This version of Windows does not support configuration of LongPaths.' + return $false + } $key = Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem' -ErrorAction SilentlyContinue -Name 'LongPathsEnabled' if (!$key -or ($key.LongPathsEnabled -eq 0)) { warn 'LongPaths support is not enabled.' diff --git a/lib/install.ps1 b/lib/install.ps1 index e4202cd76b..88c1e8a0ae 100644 --- a/lib/install.ps1 +++ b/lib/install.ps1 @@ -34,6 +34,14 @@ function install_app($app, $architecture, $global, $suggested, $use_cache = $tru return } + if ((get_config 'manifest-review' $false) -and ($MyInvocation.ScriptName -notlike '*scoop-update*')) { + Write-Output "Manifest: $app.json" + Write-Output $manifest | ConvertToPrettyJson + $answer = Read-Host -Prompt "Continue installation? [Y/n]" + if (($answer -eq 'n') -or ($answer -eq 'N')) { + return + } + } write-output "Installing '$app' ($version) [$architecture]" $dir = ensure (versiondir $app $version $global) @@ -870,18 +878,9 @@ function create_shims($manifest, $dir, $global, $arch) { } function rm_shim($name, $shimdir) { - $shim = "$shimdir\$name.ps1" - - if(!(test-path $shim)) { # handle no shim from failed install - warn "Shim for '$name' is missing. Skipping." - } else { - write-output "Removing shim for '$name'." - Remove-Item $shim - } - - # other shim types might be present - '', '.exe', '.shim', '.cmd' | ForEach-Object { + '', '.exe', '.shim', '.cmd', '.ps1' | ForEach-Object { if(test-path -Path "$shimdir\$name$_" -PathType leaf) { + Write-Output "Removing shim '$name$_'." Remove-Item "$shimdir\$name$_" } } @@ -988,11 +987,16 @@ function find_dir_or_subdir($path, $dir) { function env_add_path($manifest, $dir, $global, $arch) { $env_add_path = arch_specific 'env_add_path' $manifest $arch + $dir = $dir.TrimEnd('\') if ($env_add_path) { # GH-3785: Add path in ascending order. [Array]::Reverse($env_add_path) $env_add_path | Where-Object { $_ } | ForEach-Object { - $path_dir = Join-Path $dir $_ + if ($_ -eq '.') { + $path_dir = $dir + } else { + $path_dir = Join-Path $dir $_ + } if (!(is_in_dir $dir $path_dir)) { abort "Error in manifest: env_add_path '$_' is outside the app directory." @@ -1076,7 +1080,7 @@ function prune_installed($apps, $global) { # check whether the app failed to install function failed($app, $global) { if (is_directory (appdir $app $global)) { - return !(install_info $app (current_version $app $global) $global) + return !(install_info $app (Select-CurrentVersion -AppName $app -Global:$global) $global) } else { return $false } diff --git a/lib/json.ps1 b/lib/json.ps1 index f3b6c97f1a..8f561a8b1d 100644 --- a/lib/json.ps1 +++ b/lib/json.ps1 @@ -92,31 +92,33 @@ function ConvertToPrettyJson { } } -function json_path([String] $json, [String] $jsonpath, [Hashtable] $substitutions) { +function json_path([String] $json, [String] $jsonpath, [Hashtable] $substitutions, [Boolean] $reverse, [Boolean] $single) { Add-Type -Path "$psscriptroot\..\supporting\validator\bin\Newtonsoft.Json.dll" if ($null -ne $substitutions) { $jsonpath = substitute $jsonpath $substitutions ($jsonpath -like "*=~*") } try { - $obj = [Newtonsoft.Json.Linq.JObject]::Parse($json) + $obj = [Newtonsoft.Json.Linq.JValue]::Parse($json) } catch [Newtonsoft.Json.JsonReaderException] { - try { - $obj = [Newtonsoft.Json.Linq.JArray]::Parse($json) - } catch [Newtonsoft.Json.JsonReaderException] { - return $null - } + return $null } - try { - try { - $result = $obj.SelectToken($jsonpath, $true) - } catch [Newtonsoft.Json.JsonException] { - return $null + $result = $obj.SelectTokens($jsonpath, $true) + if ($reverse) { + # Return versions in reverse order + $result = [System.Linq.Enumerable]::Reverse($result) } - return $result.ToString() - } catch [System.Management.Automation.MethodInvocationException] { - write-host -f DarkRed $_ - return $null + if ($single) { + # Extract First value + $result = [System.Linq.Enumerable]::First($result) + # Convert first value to string + $result = $result.ToString() + } else { + $result = "$([String]::Join('\n', $result))" + } + return $result + } catch [Exception] { + Write-Host $_ -ForegroundColor DarkRed } return $null @@ -160,7 +162,7 @@ function normalize_values([psobject] $json) { # Recursively edit psobjects # If the values is psobjects, its not normalized # For example if manifest have architecture and it's architecture have array with single value it's not formatted. - # @see https://github.com/lukesampson/scoop/pull/2642#issue-220506263 + # @see https://github.com/ScoopInstaller/Scoop/pull/2642#issue-220506263 if ($_.Value -is [System.Management.Automation.PSCustomObject]) { $_.Value = normalize_values $_.Value } diff --git a/lib/versions.ps1 b/lib/versions.ps1 index 5b2583d87e..d1437c7223 100644 --- a/lib/versions.ps1 +++ b/lib/versions.ps1 @@ -135,7 +135,7 @@ function Compare-Version { $Delimiter = '-' ) process { - # Use '+' sign as post-release, see https://github.com/lukesampson/scoop/pull/3721#issuecomment-553718093 + # Use '+' sign as post-release, see https://github.com/ScoopInstaller/Scoop/pull/3721#issuecomment-553718093 $ReferenceVersion, $DifferenceVersion = @($ReferenceVersion, $DifferenceVersion) -replace '\+', '-' # Return 0 if versions are equal diff --git a/libexec/scoop-alias.ps1 b/libexec/scoop-alias.ps1 index 8e67ab766e..6efafe8c0d 100644 --- a/libexec/scoop-alias.ps1 +++ b/libexec/scoop-alias.ps1 @@ -16,22 +16,22 @@ # -v, --verbose Show alias description and table headers (works only for 'list') param( - [String]$opt, - [String]$name, - [String]$command, - [String]$description, - [Switch]$verbose = $false + [String]$opt, + [String]$name, + [String]$command, + [String]$description, + [Switch]$verbose = $false ) . "$psscriptroot\..\lib\core.ps1" . "$psscriptroot\..\lib\help.ps1" . "$psscriptroot\..\lib\install.ps1" -$script:config_alias = "alias" +$script:config_alias = 'alias' function init_alias_config { $aliases = get_config $script:config_alias - if(!$aliases) { + if (!$aliases) { $aliases = @{} } @@ -39,13 +39,13 @@ function init_alias_config { } function add_alias($name, $command) { - if(!$command) { + if (!$command) { abort "Can't create an empty alias." } # get current aliases from config $aliases = init_alias_config - if($aliases.$name) { + if ($aliases.$name) { abort "Alias $name already exists." } @@ -54,11 +54,11 @@ function add_alias($name, $command) { # generate script $shimdir = shimdir $false $script = -@" + @" # Summary: $description $command "@ - $script | out-file "$shimdir\$alias_file.ps1" -encoding utf8 + $script | Out-File "$shimdir\$alias_file.ps1" -Encoding ASCII # add alias to config $aliases | Add-Member -MemberType NoteProperty -Name $name -Value $alias_file @@ -68,11 +68,11 @@ $command function rm_alias($name) { $aliases = init_alias_config - if(!$name) { - abort "Which alias should be removed?" + if (!$name) { + abort 'Which alias should be removed?' } - if($aliases.$name) { + if ($aliases.$name) { "Removing alias $name..." rm_shim $aliases.$name (shimdir $false) @@ -92,24 +92,24 @@ function list_aliases { $command = ($content | Select-Object -Skip 1).Trim() $summary = (summary $content).Trim() - $aliases += New-Object psobject -Property @{Name=$_.name; Summary=$summary; Command=$command} + $aliases += New-Object psobject -Property @{Name = $_.name; Summary = $summary; Command = $command } } - if(!$aliases.count) { - warn "No aliases founds." + if (!$aliases.count) { + warn 'No aliases founds.' } $aliases = $aliases.GetEnumerator() | Sort-Object Name - if($verbose) { - return $aliases | Select-Object Name, Command, Summary | Format-Table -autosize -wrap + if ($verbose) { + return $aliases | Select-Object Name, Command, Summary | Format-Table -AutoSize -Wrap } else { - return $aliases | Select-Object Name, Command | Format-Table -autosize -hidetablehead -wrap + return $aliases | Select-Object Name, Command | Format-Table -AutoSize -hidetablehead -Wrap } } -switch($opt) { - "add" { add_alias $name $command } - "rm" { rm_alias $name } - "list" { list_aliases } +switch ($opt) { + 'add' { add_alias $name $command } + 'rm' { rm_alias $name } + 'list' { list_aliases } default { my_usage; exit 1 } } diff --git a/libexec/scoop-bucket.ps1 b/libexec/scoop-bucket.ps1 index 2c75cb7c2a..0d5e337dbf 100644 --- a/libexec/scoop-bucket.ps1 +++ b/libexec/scoop-bucket.ps1 @@ -10,7 +10,7 @@ # scoop bucket add [] # # e.g.: -# scoop bucket add extras https://github.com/lukesampson/scoop-extras.git +# scoop bucket add extras https://github.com/ScoopInstaller/Extras.git # # Since the 'extras' bucket is known to Scoop, this can be shortened to: # scoop bucket add extras diff --git a/libexec/scoop-cat.ps1 b/libexec/scoop-cat.ps1 new file mode 100644 index 0000000000..f3a6b3e533 --- /dev/null +++ b/libexec/scoop-cat.ps1 @@ -0,0 +1,23 @@ +# Usage: scoop cat +# Summary: Show content of specified manifest. + +param($app) + +. "$psscriptroot\..\lib\manifest.ps1" +. "$psscriptroot\..\lib\install.ps1" +. "$psscriptroot\..\lib\help.ps1" + +reset_aliases + +if (!$app) { error ' missing'; my_usage; exit 1 } + +$app, $bucket, $null = parse_app $app +$app, $manifest, $bucket, $url = Find-Manifest $app $bucket + +if ($manifest) { + $manifest | ConvertToPrettyJson | Write-Host +} else { + abort "Couldn't find manifest for '$app'$(if($url) { " at the URL $url" })." +} + +exit $exitCode diff --git a/libexec/scoop-config.ps1 b/libexec/scoop-config.ps1 index c0fb6b39c5..c71e46498e 100644 --- a/libexec/scoop-config.ps1 +++ b/libexec/scoop-config.ps1 @@ -17,14 +17,98 @@ # Settings # -------- # +# 7ZIPEXTRACT_USE_EXTERNAL: $true|$false +# External 7zip (from path) will be used for archives extraction. +# +# MSIEXTRACT_USE_LESSMSI: $true|$false +# Prefer lessmsi utility over native msiexec. +# +# NO_JUNCTIONS: $true|$false +# The 'current' version alias will not be used. Shims and shortcuts will point to specific version instead. +# +# SCOOP_REPO: http://github.com/ScoopInstaller/Scoop +# Git repository containining scoop source code. +# This configuration is useful for custom forks. +# +# SCOOP_BRANCH: master|develop +# Allow to use different branch than master. +# Could be used for testing specific functionalities before released into all users. +# If you want to receive updates earlier to test new functionalities use develop (see: 'https://github.com/ScoopInstaller/Scoop/issues/2939') +# # proxy: [username:password@]host:port +# By default, Scoop will use the proxy settings from Internet Options, but with anonymous authentication. +# +# * To use the credentials for the current logged-in user, use 'currentuser' in place of username:password +# * To use the system proxy settings configured in Internet Options, use 'default' in place of host:port +# * An empty or unset value for proxy is equivalent to 'default' (with no username or password) +# * To bypass the system proxy and connect directly, use 'none' (with no username or password) +# +# default-architecture: 64bit|32bit +# Allow to configure preferred architecture for application installation. +# If not specified, architecture is determined be system. +# +# debug: $true|$false +# Additional and detailed output will be shown. +# +# force-update: $true|$false +# Force apps updating to bucket's version. +# +# show_update_log: $true|$false +# Do not show changed commits on 'scoop update' +# +# manifest_review: $true|$false +# Displays the manifest of every app that's about to +# be installed, then asks user if they wish to proceed. +# +# shim: kiennq|scoopcs|71 +# Choose scoop shim build. +# +# rootPath: $Env:UserProfile\scoop +# Path to Scoop root directory. +# +# globalPath: $Env:ProgramData\scoop +# Path to Scoop root directory for global apps. +# +# cachePath: +# For downloads, defaults to 'cache' folder under Scoop root directory. +# +# checkver_token: +# GitHub API token used to make authenticated requests. +# This is essential for checkver and similar functions +# to run without incurring rate limits. +# +# virustotal_api_key: +# API key used for uploading/scanning files using virustotal. +# See: 'https://support.virustotal.com/hc/en-us/articles/115002088769-Please-give-me-an-API-key' +# +# ARIA2 configuration +# ------------------- +# +# aria2-enabled: $true|$false +# Aria2c will be used for downloading of artifacts. +# +# aria2-warning-enabled: $true|$false +# Disable Aria2c warning which is shown while downloading. +# +# aria2-retry-wait: 2 +# Number of seconds to wait between retries. +# See: 'https://aria2.github.io/manual/en/html/aria2c.html#cmdoption-retry-wait' +# +# aria2-split: 5 +# Number of connections used for downlaod. +# See: 'https://aria2.github.io/manual/en/html/aria2c.html#cmdoption-s' +# +# aria2-max-connection-per-server: 5 +# The maximum number of connections to one server for each download. +# See: 'https://aria2.github.io/manual/en/html/aria2c.html#cmdoption-x' # -# By default, Scoop will use the proxy settings from Internet Options, but with anonymous authentication. +# aria2-min-split-size: 5M +# Downloaded files will be splitted by this configured size and downloaded using multiple connections. +# See: 'https://aria2.github.io/manual/en/html/aria2c.html#cmdoption-k' # -# * To use the credentials for the current logged-in user, use 'currentuser' in place of username:password -# * To use the system proxy settings configured in Internet Options, use 'default' in place of host:port -# * An empty or unset value for proxy is equivalent to 'default' (with no username or password) -# * To bypass the system proxy and connect directly, use 'none' (with no username or password) +# aria2-options: +# Array of additional aria2 options. +# See: 'https://aria2.github.io/manual/en/html/aria2c.html#options' param($name, $value) diff --git a/libexec/scoop-create.ps1 b/libexec/scoop-create.ps1 index fa8b1cf9ed..3a33d09ea0 100644 --- a/libexec/scoop-create.ps1 +++ b/libexec/scoop-create.ps1 @@ -11,29 +11,28 @@ function create_manifest($url) { $url_parts = $null try { $url_parts = parse_url $url - } - catch { + } catch { abort "Error: $url is not a valid URL" } - $name = choose_item $url_parts "App name" + $name = choose_item $url_parts 'App name' $name = if ($name.Length -gt 0) { $name - } - else { - file_name ($url_parts | select-object -last 1) + } else { + file_name ($url_parts | Select-Object -Last 1) } - $manifest.version = choose_item $url_parts "Version" + $manifest.version = choose_item $url_parts 'Version' - $manifest | convertto-json | out-file -filepath "$name.json" -encoding utf8 - $manifest_path = join-path $pwd "$name.json" - write-host "Created '$manifest_path'." + $manifest | ConvertTo-Json | Out-File -FilePath "$name.json" -Encoding ASCII + $manifest_path = Join-Path $pwd "$name.json" + Write-Host "Created '$manifest_path'." } function new_manifest() { - @{ "homepage" = ""; "license" = ""; "version" = ""; "url" = ""; - "hash" = ""; "extract_dir" = ""; "bin" = ""; "depends" = "" } + @{ 'homepage' = ''; 'license' = ''; 'version' = ''; 'url' = ''; + 'hash' = ''; 'extract_dir' = ''; 'bin' = ''; 'depends' = '' + } } function file_name($segment) { @@ -41,19 +40,19 @@ function file_name($segment) { } function parse_url($url) { - $uri = new-object Uri $url - $uri.pathandquery.substring(1).split("/") + $uri = New-Object Uri $url + $uri.pathandquery.substring(1).split('/') } function choose_item($list, $query) { for ($i = 0; $i -lt $list.count; $i++) { $item = $list[$i] - write-host "$($i + 1)) $item" + Write-Host "$($i + 1)) $item" } - $sel = read-host $query + $sel = Read-Host $query if ($sel.trim() -match '^[0-9+]$') { - return $list[$sel-1] + return $list[$sel - 1] } $sel @@ -61,8 +60,7 @@ function choose_item($list, $query) { if (!$url) { scoop help create -} -else { +} else { create_manifest $url } diff --git a/libexec/scoop-update.ps1 b/libexec/scoop-update.ps1 index b84c6ab99d..c307b7435d 100644 --- a/libexec/scoop-update.ps1 +++ b/libexec/scoop-update.ps1 @@ -28,7 +28,7 @@ reset_aliases -$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', 'quiet', 'all' if ($err) { "scoop update: $err"; exit 1 } $global = $opt.g -or $opt.global $force = $opt.f -or $opt.force @@ -41,7 +41,7 @@ $all = $opt.a -or $opt.all # load config $configRepo = get_config SCOOP_REPO if (!$configRepo) { - $configRepo = "https://github.com/lukesampson/scoop" + $configRepo = "https://github.com/ScoopInstaller/Scoop" set_config SCOOP_REPO $configRepo | Out-Null } @@ -199,7 +199,7 @@ function update($app, $global, $quiet = $false, $independent, $suggested, $use_c write-host "Updating '$app' ($old_version -> $version)" # region Workaround - # Workaround for https://github.com/lukesampson/scoop/issues/2220 until install is refactored + # Workaround for https://github.com/ScoopInstaller/Scoop/issues/2220 until install is refactored # Remove and replace whole region after proper fix Write-Host "Downloading new version" if (Test-Aria2Enabled) { @@ -278,7 +278,7 @@ function update($app, $global, $quiet = $false, $independent, $suggested, $use_c install_app $app $architecture $global $suggested $use_cache $check_hash } -if (!$apps) { +if (-not ($apps -or $all)) { if ($global) { "scoop update: --global is invalid when is not specified."; exit 1 } diff --git a/libexec/scoop-which.ps1 b/libexec/scoop-which.ps1 index 9f88b67517..4bee071b2a 100644 --- a/libexec/scoop-which.ps1 +++ b/libexec/scoop-which.ps1 @@ -19,10 +19,15 @@ $path = "$($gcm.path)" $usershims = "$(resolve-path $(shimdir $false))" $globalshims = fullpath (shimdir $true) # don't resolve: may not exist -if($path.endswith(".ps1") -and ($path -like "$usershims*" -or $path -like "$globalshims*")) { - $shimtext = Get-Content $path - - $exepath = ($shimtext | Where-Object { $_.startswith('$path') }).split(' ') | Select-Object -Last 1 | Invoke-Expression +if($path -like "$usershims*" -or $path -like "$globalshims*") { + $exepath = if ($path.endswith(".exe") -or $path.endswith(".shim")) { + (Get-Content ($path -replace '\.exe$', '.shim') | Select-Object -First 1).replace('path = ', '') + } else { + ((Select-String -Path $path -Pattern '^(?:@rem|#)\s*(.*)$').Matches.Groups | Select-Object -Index 1).Value + } + if (!$exepath) { + $exepath = ((Select-String -Path $path -Pattern '[''"]([^@&]*?)[''"]' -AllMatches).Matches.Groups | Select-Object -Last 1).Value + } if(![system.io.path]::ispathrooted($exepath)) { # Expand relative path diff --git a/schema.json b/schema.json index fb01dd6079..99c5c3d8e2 100644 --- a/schema.json +++ b/schema.json @@ -220,6 +220,9 @@ }, "persist": { "$ref": "#/definitions/stringOrArrayOfStringsOrAnArrayOfArrayOfStrings" + }, + "license": { + "$ref": "#/definitions/license" }, "note": { "$ref": "#/definitions/stringOrArrayOfStrings" @@ -518,7 +521,7 @@ "type": "string" }, "innosetup": { - "description": "True if the installer InnoSetup based. Found in https://github.com/lukesampson/scoop/search?l=JSON&q=innosetup", + "description": "True if the installer InnoSetup based. Found in https://github.com/ScoopInstaller/Main/search?l=JSON&q=innosetup", "type": "boolean" }, "installer": { @@ -593,7 +596,10 @@ } }, "required": [ - "version" + "version", + "description", + "homepage", + "license" ], "title": "scoop app manifest schema", "type": "object" diff --git a/test/Scoop-Core.Tests.ps1 b/test/Scoop-Core.Tests.ps1 index 04bab9c478..5046ef64fa 100644 --- a/test/Scoop-Core.Tests.ps1 +++ b/test/Scoop-Core.Tests.ps1 @@ -221,7 +221,7 @@ describe "rm_shim" -Tag 'Scoop' { } } -Describe "get_app_name_from_ps1_shim" -Tag 'Scoop' { +Describe "get_app_name_from_shim" -Tag 'Scoop' { BeforeAll { $working_dir = setup_working "shim" $shimdir = shimdir @@ -229,7 +229,7 @@ Describe "get_app_name_from_ps1_shim" -Tag 'Scoop' { } It "returns empty string if file does not exist" -skip:$isUnix { - get_app_name_from_ps1_shim "non-existent-file" | should -be "" + get_app_name_from_shim "non-existent-file" | should -be "" } It "returns app name if file exists and is a shim to an app" -skip:$isUnix { @@ -237,12 +237,12 @@ Describe "get_app_name_from_ps1_shim" -Tag 'Scoop' { Write-Output "" | Out-File "$working_dir/mockapp/current/mockapp.ps1" shim "$working_dir/mockapp/current/mockapp.ps1" $false "shim-test" $shim_path = (get-command "shim-test.ps1").Path - get_app_name_from_ps1_shim "$shim_path" | should -be "mockapp" + get_app_name_from_shim "$shim_path" | should -be "mockapp" } It "returns empty string if file exists and is not a shim" -skip:$isUnix { Write-Output "lorem ipsum" | Out-File -Encoding ascii "$working_dir/mock-shim.ps1" - get_app_name_from_ps1_shim "$working_dir/mock-shim.ps1" | should -be "" + get_app_name_from_shim "$working_dir/mock-shim.ps1" | should -be "" } AfterEach { @@ -269,7 +269,7 @@ describe "ensure_robocopy_in_path" -Tag 'Scoop' { ensure_robocopy_in_path - "$shimdir/robocopy.ps1" | should -exist + # "$shimdir/robocopy.ps1" | should -exist "$shimdir/robocopy.exe" | should -exist # clean up @@ -284,7 +284,7 @@ describe "ensure_robocopy_in_path" -Tag 'Scoop' { ensure_robocopy_in_path - "$shimdir/robocopy.ps1" | should -not -exist + # "$shimdir/robocopy.ps1" | should -not -exist "$shimdir/robocopy.exe" | should -not -exist } }