'."
41 | }
42 | abort "Couldn't find manifest for '$AppName'$(if($bucket) { " from '$bucket' bucket" } elseif($url) { " at '$url'" })."
43 | }
44 |
45 | $deps = @(Get-InstallationHelper $manifest $Architecture) + @($manifest.depends) | Select-Object -Unique
46 |
47 | foreach ($dep in $deps) {
48 | if ($Resolved -notcontains $dep) {
49 | if ($Unresolved -contains $dep) {
50 | abort "Circular dependency detected: '$AppName' -> '$dep'."
51 | }
52 | $Resolved, $Unresolved = Get-Dependency $dep $Architecture -Resolved $Resolved -Unresolved $Unresolved
53 | }
54 | }
55 |
56 | $Unresolved = $Unresolved -ne $AppName
57 | if ($bucket) {
58 | $Resolved += "$bucket/$AppName"
59 | } else {
60 | if ($url) {
61 | $Resolved += $url
62 | } else {
63 | $Resolved += $AppName
64 | }
65 | }
66 | if ($Unresolved.Length -eq 0) {
67 | return $Resolved
68 | } else {
69 | return $Resolved, $Unresolved
70 | }
71 | }
72 | }
73 |
74 | function Get-InstallationHelper {
75 | <#
76 | .SYNOPSIS
77 | Get helpers that used in installation
78 | .PARAMETER Manifest
79 | App's manifest
80 | .PARAMETER Architecture
81 | Architecture of the app
82 | .PARAMETER All
83 | If true, return all helpers, otherwise return only helpers that are not already installed
84 | .OUTPUTS
85 | [Object[]]
86 | List of helpers
87 | #>
88 | [CmdletBinding()]
89 | [OutputType([Object[]])]
90 | param (
91 | [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
92 | [PSObject]
93 | $Manifest,
94 | [Parameter(Mandatory = $true, Position = 1)]
95 | [String]
96 | $Architecture,
97 | [Switch]
98 | $All
99 | )
100 | begin {
101 | $helper = @()
102 | }
103 | process {
104 | $url = arch_specific 'url' $Manifest $Architecture
105 | $pre_install = arch_specific 'pre_install' $Manifest $Architecture
106 | $installer = arch_specific 'installer' $Manifest $Architecture
107 | $post_install = arch_specific 'post_install' $Manifest $Architecture
108 | $script = $pre_install + $installer.script + $post_install
109 | if (((Test-7zipRequirement -Uri $url) -or ($script -like '*Expand-7zipArchive *')) -and !(get_config USE_EXTERNAL_7ZIP)) {
110 | $helper += '7zip'
111 | }
112 | if (((Test-LessmsiRequirement -Uri $url) -or ($script -like '*Expand-MsiArchive *')) -and (get_config USE_LESSMSI)) {
113 | $helper += 'lessmsi'
114 | }
115 | if ($Manifest.innosetup -or ($script -like '*Expand-InnoArchive *')) {
116 | $helper += 'innounp'
117 | }
118 | if ($script -like '*Expand-DarkArchive *') {
119 | $helper += 'dark'
120 | }
121 | if (!$All) {
122 | '7zip', 'lessmsi', 'innounp', 'dark' | ForEach-Object {
123 | if (Test-HelperInstalled -Helper $_) {
124 | $helper = $helper -ne $_
125 | }
126 | }
127 | }
128 | }
129 | end {
130 | return $helper
131 | }
132 | }
133 |
134 | function Test-7zipRequirement {
135 | [CmdletBinding()]
136 | [OutputType([Boolean])]
137 | param (
138 | [Parameter(Mandatory = $true)]
139 | [AllowNull()]
140 | [String[]]
141 | $Uri
142 | )
143 | return ($Uri | Where-Object {
144 | $_ -match '\.(001|7z|bz(ip)?2?|gz|img|iso|lzma|lzh|nupkg|rar|tar|t[abgpx]z2?|t?zst|xz)(\.[^\d.]+)?$'
145 | }).Count -gt 0
146 | }
147 |
148 | function Test-LessmsiRequirement {
149 | [CmdletBinding()]
150 | [OutputType([Boolean])]
151 | param (
152 | [Parameter(Mandatory = $true)]
153 | [AllowNull()]
154 | [String[]]
155 | $Uri
156 | )
157 | return ($Uri | Where-Object { $_ -match '\.msi$' }).Count -gt 0
158 | }
159 |
--------------------------------------------------------------------------------
/lib/description.ps1:
--------------------------------------------------------------------------------
1 | function find_description($url, $html, $redir = $false) {
2 | $meta = meta_tags $html
3 |
4 | # check
5 | $og_description = meta_content $meta 'property' 'og:description'
6 | if($og_description) {
7 | return $og_description, ''
8 | }
9 |
10 | # check
11 | $description = meta_content $meta 'name' 'description'
12 | if($description) {
13 | return $description, ''
14 | }
15 |
16 | # check redirect
17 | $refresh = meta_refresh $meta $url
18 | if($refresh -and !$redir) {
19 | $wc = New-Object Net.Webclient
20 | $wc.Headers.Add('User-Agent', (Get-UserAgent))
21 | $data = $wc.DownloadData($refresh)
22 | $html = (Get-Encoding($wc)).GetString($data)
23 | return find_description $refresh $html $true
24 | }
25 |
26 | # check text for 'x is ...'
27 | $text = html_text $html $meta
28 | $text_desc = find_is $text
29 | if($text_desc) {
30 | return $text_desc, 'text'
31 | }
32 |
33 | # first paragraph
34 | $first_para = first_para $html
35 | if($first_para) {
36 | return $first_para, 'first '
37 | }
38 |
39 | return $null, $null
40 | }
41 |
42 | function clean_description($description) {
43 | if(!$description) { return $description }
44 | $description = $description -replace '\n', ' '
45 | $description = $description -replace '\s{2,}', ' '
46 | return $description.trim()
47 | }
48 |
49 | # Collects meta tags from $html into hashtables.
50 | function meta_tags($html) {
51 | $tags = @()
52 | $meta = ([regex]']+>').matches($html)
53 | $meta | ForEach-Object {
54 | $attrs = ([regex]'([\w-]+)="([^"]+)"').matches($_.value)
55 | $hash = @{}
56 | $attrs | ForEach-Object {
57 | $hash[$_.groups[1].value] = $_.groups[2].value
58 | }
59 | $tags += $hash
60 | }
61 | $tags
62 | }
63 |
64 | function meta_content($tags, $attribute, $search) {
65 | if(!$tags) { return }
66 | return $tags | Where-Object { $_[$attribute] -eq $search } | ForEach-Object { $_['content'] }
67 | }
68 |
69 | # Looks for a redirect URL in a refresh tag.
70 | function meta_refresh($tags, $url) {
71 | $refresh = meta_content $tags 'http-equiv' 'refresh'
72 | if($refresh) {
73 | if($refresh -match '\d+;\s*url\s*=\s*(.*)') {
74 | $refresh_url = $matches[1].trim("'", '"')
75 | if($refresh_url -notmatch '^https?://') {
76 | $refresh_url = "$url$refresh_url"
77 | }
78 | return $refresh_url
79 | }
80 | }
81 | }
82 |
83 | function html_body($html) {
84 | if($html -match '(?s)
]*>(.*?)') {
85 | $body = $matches[1]
86 | $body = $body -replace '(?s)', ' '
87 | $body = $body -replace '(?s)', ' '
88 | return $body
89 | }
90 | }
91 |
92 | function html_text($body, $meta_tags) {
93 | $body = html_body $html
94 | if($body) {
95 | return strip_html $body
96 | }
97 | }
98 |
99 | function strip_html($html) {
100 | $html = $html -replace '(?s)<[^>]*>', ' '
101 | $html = $html -replace '\t', ' '
102 | $html = $html -replace ' ?', ' '
103 | $html = $html -replace '>?', '>'
104 | $html = $html -replace '<?', '<'
105 | $html = $html -replace '"?', '"'
106 |
107 | $encoding_meta = meta_content $meta_tags 'http-equiv' 'Content-Type'
108 | if($encoding_meta) {
109 | if($encoding_meta -match 'charset\s*=\s*(.*)') {
110 | $charset = $matches[1]
111 | try {
112 | $encoding = [text.encoding]::getencoding($charset)
113 | } catch {
114 | Write-Warning "Unknown charset"
115 | }
116 | if($encoding) {
117 | $html = ([regex]'(\d+);?').replace($html, {
118 | param($m)
119 | try {
120 | return $encoding.getstring($m.Groups[1].Value)
121 | } catch {
122 | return $m.value
123 | }
124 | })
125 | }
126 | }
127 | }
128 |
129 | $html = $html -replace '\n +', "`r`n"
130 | $html = $html -replace '\n{2,}', "`r`n"
131 | $html = $html -replace ' {2,}', ' '
132 | $html = $html -replace ' (\.|,)', '$1'
133 | return $html.trim()
134 | }
135 |
136 | function find_is($text) {
137 | if($text -match '(?s)[\n\.]((?:[^\n\.])+? is .+?[\.!])') {
138 | return $matches[1].trim()
139 | }
140 | }
141 |
142 | function first_para($html) {
143 | $body = html_body $html
144 | if($body -match '(?s)]*>(.*?)
') {
145 | return strip_html $matches[1]
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/lib/diagnostic.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | Diagnostic tests.
3 | Return $true if the test passed, otherwise $false.
4 | Use 'warn' to highlight the issue, and follow up with the recommended actions to rectify.
5 | #>
6 | function check_windows_defender($global) {
7 | $defender = Get-Service -Name WinDefend -ErrorAction SilentlyContinue
8 | if (Test-CommandAvailable Get-MpPreference) {
9 | if ((Get-MpPreference).DisableRealtimeMonitoring) { return $true }
10 | if ($defender -and $defender.Status) {
11 | if ($defender.Status -eq [System.ServiceProcess.ServiceControllerStatus]::Running) {
12 | $installPath = $scoopdir;
13 | if ($global) { $installPath = $globaldir; }
14 |
15 | $exclusionPath = (Get-MpPreference).ExclusionPath
16 | if (!($exclusionPath -contains $installPath)) {
17 | info "Windows Defender may slow down or disrupt installs with realtime scanning."
18 | Write-Host " Consider running:"
19 | Write-Host " sudo Add-MpPreference -ExclusionPath '$installPath'"
20 | Write-Host " (Requires 'sudo' command. Run 'scoop install sudo' if you don't have it.)"
21 | return $false
22 | }
23 | }
24 | }
25 | }
26 | return $true
27 | }
28 |
29 | function check_main_bucket {
30 | if ((Get-LocalBucket) -notcontains 'main') {
31 | warn 'Main bucket is not added.'
32 | Write-Host " run 'scoop bucket add main'"
33 |
34 | return $false
35 | }
36 |
37 | return $true
38 | }
39 |
40 | function check_long_paths {
41 | if ([System.Environment]::OSVersion.Version.Major -lt 10 -or [System.Environment]::OSVersion.Version.Build -lt 1607) {
42 | warn 'This version of Windows does not support configuration of LongPaths.'
43 | return $false
44 | }
45 | $key = Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem' -ErrorAction SilentlyContinue -Name 'LongPathsEnabled'
46 | if (!$key -or ($key.LongPathsEnabled -eq 0)) {
47 | warn 'LongPaths support is not enabled.'
48 | Write-Host " You can enable it by running:"
49 | Write-Host " sudo Set-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem' -Name 'LongPathsEnabled' -Value 1"
50 | Write-Host " (Requires 'sudo' command. Run 'scoop install sudo' if you don't have it.)"
51 | return $false
52 | }
53 |
54 | return $true
55 | }
56 |
57 | function Get-WindowsDeveloperModeStatus {
58 | $DevModRegistryPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock"
59 | if (!(Test-Path -Path $DevModRegistryPath) -or (Get-ItemProperty -Path `
60 | $DevModRegistryPath -Name AllowDevelopmentWithoutDevLicense -ErrorAction `
61 | SilentlyContinue).AllowDevelopmentWithoutDevLicense -ne 1) {
62 | warn "Windows Developer Mode is not enabled. Operations relevant to symlinks may fail without proper rights."
63 | Write-Host " You may read more about the symlinks support here:"
64 | Write-Host " https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/"
65 | return $false
66 | }
67 |
68 | return $true
69 | }
70 |
--------------------------------------------------------------------------------
/lib/getopt.ps1:
--------------------------------------------------------------------------------
1 | # adapted from http://hg.python.org/cpython/file/2.7/Lib/getopt.py
2 | # argv:
3 | # array of arguments
4 | # shortopts:
5 | # string of single-letter options. options that take a parameter
6 | # should be follow by ':'
7 | # longopts:
8 | # array of strings that are long-form options. options that take
9 | # a parameter should end with '='
10 | # returns @(opts hash, remaining_args array, error string)
11 | # NOTES:
12 | # The first "--" in $argv, if any, will terminate all options; any
13 | # following arguments are treated as non-option arguments, even if
14 | # they begin with a hyphen. The "--" itself will not be included in
15 | # the returned $opts. (POSIX-compatible)
16 | function getopt([String[]]$argv, [String]$shortopts, [String[]]$longopts) {
17 | $opts = @{}; $rem = @()
18 |
19 | function err($msg) {
20 | $opts, $rem, $msg
21 | }
22 |
23 | function regex_escape($str) {
24 | return [Regex]::Escape($str)
25 | }
26 |
27 | for ($i = 0; $i -lt $argv.Length; $i++) {
28 | $arg = $argv[$i]
29 | if ($null -eq $arg) { continue }
30 | # don't try to parse array arguments
31 | if ($arg -is [Array]) { $rem += , $arg; continue }
32 | if ($arg -is [Int]) { $rem += $arg; continue }
33 | if ($arg -is [Decimal]) { $rem += $arg; continue }
34 |
35 | if ($arg -eq '--') {
36 | if ($i -lt $argv.Length - 1) {
37 | $rem += $argv[($i + 1)..($argv.Length - 1)]
38 | }
39 | break
40 | } elseif ($arg.StartsWith('--')) {
41 | $name = $arg.Substring(2)
42 |
43 | $longopt = $longopts | Where-Object { $_ -match "^$name=?$" }
44 |
45 | if ($longopt) {
46 | if ($longopt.EndsWith('=')) {
47 | # requires arg
48 | if ($i -eq $argv.Length - 1) {
49 | return err "Option --$name requires an argument."
50 | }
51 | $opts.$name = $argv[++$i]
52 | } else {
53 | $opts.$name = $true
54 | }
55 | } else {
56 | return err "Option --$name not recognized."
57 | }
58 | } elseif ($arg.StartsWith('-') -and $arg -ne '-') {
59 | for ($j = 1; $j -lt $arg.Length; $j++) {
60 | $letter = $arg[$j].ToString()
61 |
62 | if ($shortopts -match "$(regex_escape $letter)`:?") {
63 | $shortopt = $Matches[0]
64 | if ($shortopt[1] -eq ':') {
65 | if ($j -ne $arg.Length - 1 -or $i -eq $argv.Length - 1) {
66 | return err "Option -$letter requires an argument."
67 | }
68 | $opts.$letter = $argv[++$i]
69 | } else {
70 | $opts.$letter = $true
71 | }
72 | } else {
73 | return err "Option -$letter not recognized."
74 | }
75 | }
76 | } else {
77 | $rem += $arg
78 | }
79 | }
80 | $opts, $rem
81 | }
82 |
--------------------------------------------------------------------------------
/lib/help.ps1:
--------------------------------------------------------------------------------
1 | function usage($text) {
2 | $text | Select-String '(?m)^# Usage: ([^\n]*)$' | ForEach-Object { "Usage: " + $_.matches[0].groups[1].value }
3 | }
4 |
5 | function summary($text) {
6 | $text | Select-String '(?m)^# Summary: ([^\n]*)$' | ForEach-Object { $_.matches[0].groups[1].value }
7 | }
8 |
9 | function scoop_help($text) {
10 | $help_lines = $text | Select-String '(?ms)^# Help:(.(?!^[^#]))*' | ForEach-Object { $_.matches[0].value; }
11 | $help_lines -replace '(?ms)^#\s?(Help: )?', ''
12 | }
13 |
14 | function my_usage { # gets usage for the calling script
15 | usage (Get-Content $myInvocation.PSCommandPath -raw)
16 | }
17 |
--------------------------------------------------------------------------------
/lib/manifest.ps1:
--------------------------------------------------------------------------------
1 | function manifest_path($app, $bucket) {
2 | (Get-ChildItem (Find-BucketDirectory $bucket) -Filter "$(sanitary_path $app).json" -Recurse).FullName
3 | }
4 |
5 | function parse_json($path) {
6 | if ($null -eq $path -or !(Test-Path $path)) { return $null }
7 | try {
8 | Get-Content $path -Raw -Encoding UTF8 | ConvertFrom-Json -ErrorAction Stop
9 | } catch {
10 | warn "Error parsing JSON at '$path'."
11 | }
12 | }
13 |
14 | function url_manifest($url) {
15 | $str = $null
16 | try {
17 | $wc = New-Object Net.Webclient
18 | $wc.Headers.Add('User-Agent', (Get-UserAgent))
19 | $data = $wc.DownloadData($url)
20 | $str = (Get-Encoding($wc)).GetString($data)
21 | } catch [system.management.automation.methodinvocationexception] {
22 | warn "error: $($_.exception.innerexception.message)"
23 | } catch {
24 | throw
25 | }
26 | if (!$str) { return $null }
27 | try {
28 | $str | ConvertFrom-Json -ErrorAction Stop
29 | } catch {
30 | warn "Error parsing JSON at '$url'."
31 | }
32 | }
33 |
34 | function Get-Manifest($app) {
35 | $bucket, $manifest, $url = $null
36 | $app = $app.TrimStart('/')
37 | # check if app is a URL or UNC path
38 | if ($app -match '^(ht|f)tps?://|\\\\') {
39 | $url = $app
40 | $app = appname_from_url $url
41 | $manifest = url_manifest $url
42 | } else {
43 | $app, $bucket, $version = parse_app $app
44 | if ($bucket) {
45 | $manifest = manifest $app $bucket
46 | } else {
47 | foreach ($tekcub in Get-LocalBucket) {
48 | $manifest = manifest $app $tekcub
49 | if ($manifest) {
50 | $bucket = $tekcub
51 | break
52 | }
53 | }
54 | }
55 | if (!$manifest) {
56 | # couldn't find app in buckets: check if it's a local path
57 | if (Test-Path $app) {
58 | $url = Convert-Path $app
59 | $app = appname_from_url $url
60 | $manifest = url_manifest $url
61 | } else {
62 | if (($app -match '\\/') -or $app.EndsWith('.json')) { $url = $app }
63 | $app = appname_from_url $app
64 | }
65 | }
66 | }
67 | return $app, $manifest, $bucket, $url
68 | }
69 |
70 | function manifest($app, $bucket, $url) {
71 | if ($url) { return url_manifest $url }
72 | parse_json (manifest_path $app $bucket)
73 | }
74 |
75 | function save_installed_manifest($app, $bucket, $dir, $url) {
76 | if ($url) {
77 | $wc = New-Object Net.Webclient
78 | $wc.Headers.Add('User-Agent', (Get-UserAgent))
79 | $data = $wc.DownloadData($url)
80 | (Get-Encoding($wc)).GetString($data) | Out-UTF8File "$dir\manifest.json"
81 | } else {
82 | Copy-Item (manifest_path $app $bucket) "$dir\manifest.json"
83 | }
84 | }
85 |
86 | function installed_manifest($app, $version, $global) {
87 | parse_json "$(versiondir $app $version $global)\manifest.json"
88 | }
89 |
90 | function save_install_info($info, $dir) {
91 | $nulls = $info.keys | Where-Object { $null -eq $info[$_] }
92 | $nulls | ForEach-Object { $info.remove($_) } # strip null-valued
93 |
94 | $file_content = $info | ConvertToPrettyJson # in 'json.ps1'
95 | [System.IO.File]::WriteAllLines("$dir\install.json", $file_content)
96 | }
97 |
98 | function install_info($app, $version, $global) {
99 | $path = "$(versiondir $app $version $global)\install.json"
100 | if (!(Test-Path $path)) { return $null }
101 | parse_json $path
102 | }
103 |
104 | function arch_specific($prop, $manifest, $architecture) {
105 | if ($manifest.architecture) {
106 | $val = $manifest.architecture.$architecture.$prop
107 | if ($val) { return $val } # else fallback to generic prop
108 | }
109 |
110 | if ($manifest.$prop) { return $manifest.$prop }
111 | }
112 |
113 | function Get-SupportedArchitecture($manifest, $architecture) {
114 | if ($architecture -eq 'arm64' -and ($manifest | ConvertToPrettyJson) -notmatch '[''"]arm64["'']') {
115 | # Windows 10 enables existing unmodified x86 apps to run on Arm devices.
116 | # Windows 11 adds the ability to run unmodified x64 Windows apps on Arm devices!
117 | # Ref: https://learn.microsoft.com/en-us/windows/arm/overview
118 | if ($WindowsBuild -ge 22000) {
119 | # Windows 11
120 | $architecture = '64bit'
121 | } else {
122 | # Windows 10
123 | $architecture = '32bit'
124 | }
125 | }
126 | if (![String]::IsNullOrEmpty((arch_specific 'url' $manifest $architecture))) {
127 | return $architecture
128 | }
129 | }
130 |
131 | function generate_user_manifest($app, $bucket, $version) {
132 | # 'autoupdate.ps1' 'buckets.ps1' 'manifest.ps1'
133 | $app, $manifest, $bucket, $null = Get-Manifest "$bucket/$app"
134 | if ("$($manifest.version)" -eq "$version") {
135 | return manifest_path $app $bucket
136 | }
137 | warn "Given version ($version) does not match manifest ($($manifest.version))"
138 | warn "Attempting to generate manifest for '$app' ($version)"
139 |
140 | ensure (usermanifestsdir) | Out-Null
141 | $manifest_path = "$(usermanifestsdir)\$app.json"
142 |
143 | if (get_config USE_SQLITE_CACHE) {
144 | $cached_manifest = (Get-ScoopDBItem -Name $app -Bucket $bucket -Version $version).manifest
145 | if ($cached_manifest) {
146 | $cached_manifest | Out-UTF8File $manifest_path
147 | return $manifest_path
148 | }
149 | }
150 |
151 | if (!($manifest.autoupdate)) {
152 | abort "'$app' does not have autoupdate capability`r`ncouldn't find manifest for '$app@$version'"
153 | }
154 |
155 | try {
156 | Invoke-AutoUpdate $app $manifest_path $manifest $version $(@{ })
157 | return $manifest_path
158 | } catch {
159 | Write-Host -ForegroundColor DarkRed "Could not install $app@$version"
160 | }
161 |
162 | return $null
163 | }
164 |
165 | function url($manifest, $arch) { arch_specific 'url' $manifest $arch }
166 | function installer($manifest, $arch) { arch_specific 'installer' $manifest $arch }
167 | function uninstaller($manifest, $arch) { arch_specific 'uninstaller' $manifest $arch }
168 | function hash($manifest, $arch) { arch_specific 'hash' $manifest $arch }
169 | function extract_dir($manifest, $arch) { arch_specific 'extract_dir' $manifest $arch }
170 | function extract_to($manifest, $arch) { arch_specific 'extract_to' $manifest $arch }
171 |
--------------------------------------------------------------------------------
/lib/psmodules.ps1:
--------------------------------------------------------------------------------
1 | function install_psmodule($manifest, $dir, $global) {
2 | $psmodule = $manifest.psmodule
3 | if (!$psmodule) { return }
4 |
5 | $targetdir = ensure (modulesdir $global)
6 |
7 | ensure_in_psmodulepath $targetdir $global
8 |
9 | $module_name = $psmodule.name
10 | if (!$module_name) {
11 | abort "Invalid manifest: The 'name' property is missing from 'psmodule'."
12 | }
13 |
14 | $linkfrom = "$targetdir\$module_name"
15 | Write-Host "Installing PowerShell module '$module_name'"
16 |
17 | Write-Host "Linking $(friendly_path $linkfrom) => $(friendly_path $dir)"
18 |
19 | if (Test-Path $linkfrom) {
20 | warn "$(friendly_path $linkfrom) already exists. It will be replaced."
21 | Remove-Item -Path $linkfrom -Force -Recurse -ErrorAction SilentlyContinue
22 | }
23 |
24 | New-DirectoryJunction $linkfrom $dir | Out-Null
25 | }
26 |
27 | function uninstall_psmodule($manifest, $dir, $global) {
28 | $psmodule = $manifest.psmodule
29 | if (!$psmodule) { return }
30 |
31 | $module_name = $psmodule.name
32 | Write-Host "Uninstalling PowerShell module '$module_name'."
33 |
34 | $targetdir = modulesdir $global
35 |
36 | $linkfrom = "$targetdir\$module_name"
37 | if (Test-Path $linkfrom) {
38 | Write-Host "Removing $(friendly_path $linkfrom)"
39 | $linkfrom = Convert-Path $linkfrom
40 | Remove-Item -Path $linkfrom -Force -Recurse -ErrorAction SilentlyContinue
41 | }
42 | }
43 |
44 | function ensure_in_psmodulepath($dir, $global) {
45 | $path = Get-EnvVar -Name 'PSModulePath' -Global:$global
46 | if (!$global -and $null -eq $path) {
47 | $path = "$env:USERPROFILE\Documents\WindowsPowerShell\Modules"
48 | }
49 | if ($path -notmatch [Regex]::Escape($dir)) {
50 | Write-Output "Adding $(friendly_path $dir) to $(if($global){'global'}else{'your'}) PowerShell module path."
51 |
52 | Set-EnvVar -Name 'PSModulePath' -Value "$dir;$path" -Global:$global
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/lib/shortcuts.ps1:
--------------------------------------------------------------------------------
1 | # Creates shortcut for the app in the start menu
2 | function create_startmenu_shortcuts($manifest, $dir, $global, $arch) {
3 | $shortcuts = @(arch_specific 'shortcuts' $manifest $arch)
4 | $shortcuts | Where-Object { $_ -ne $null } | ForEach-Object {
5 | $target = [System.IO.Path]::Combine($dir, $_.item(0))
6 | $target = New-Object System.IO.FileInfo($target)
7 | $name = $_.item(1)
8 | $arguments = ''
9 | $icon = $null
10 | if ($_.length -ge 3) {
11 | $arguments = $_.item(2)
12 | }
13 | if ($_.length -ge 4) {
14 | $icon = [System.IO.Path]::Combine($dir, $_.item(3))
15 | $icon = New-Object System.IO.FileInfo($icon)
16 | }
17 | $arguments = (substitute $arguments @{ '$dir' = $dir; '$original_dir' = $original_dir; '$persist_dir' = $persist_dir })
18 | startmenu_shortcut $target $name $arguments $icon $global
19 | }
20 | }
21 |
22 | function shortcut_folder($global) {
23 | if ($global) {
24 | $startmenu = 'CommonStartMenu'
25 | } else {
26 | $startmenu = 'StartMenu'
27 | }
28 | return Convert-Path (ensure ([System.IO.Path]::Combine([Environment]::GetFolderPath($startmenu), 'Programs', 'Scoop Apps')))
29 | }
30 |
31 | function startmenu_shortcut([System.IO.FileInfo] $target, $shortcutName, $arguments, [System.IO.FileInfo]$icon, $global) {
32 | if (!$target.Exists) {
33 | Write-Host -f DarkRed "Creating shortcut for $shortcutName ($(fname $target)) failed: Couldn't find $target"
34 | return
35 | }
36 | if ($icon -and !$icon.Exists) {
37 | Write-Host -f DarkRed "Creating shortcut for $shortcutName ($(fname $target)) failed: Couldn't find icon $icon"
38 | return
39 | }
40 |
41 | $scoop_startmenu_folder = shortcut_folder $global
42 | $subdirectory = [System.IO.Path]::GetDirectoryName($shortcutName)
43 | if ($subdirectory) {
44 | $subdirectory = ensure $([System.IO.Path]::Combine($scoop_startmenu_folder, $subdirectory))
45 | }
46 |
47 | $wsShell = New-Object -ComObject WScript.Shell
48 | $wsShell = $wsShell.CreateShortcut("$scoop_startmenu_folder\$shortcutName.lnk")
49 | $wsShell.TargetPath = $target.FullName
50 | $wsShell.WorkingDirectory = $target.DirectoryName
51 | if ($arguments) {
52 | $wsShell.Arguments = $arguments
53 | }
54 | if ($icon -and $icon.Exists) {
55 | $wsShell.IconLocation = $icon.FullName
56 | }
57 | $wsShell.Save()
58 | Write-Host "Creating shortcut for $shortcutName ($(fname $target))"
59 | }
60 |
61 | # Removes the Startmenu shortcut if it exists
62 | function rm_startmenu_shortcuts($manifest, $global, $arch) {
63 | $shortcuts = @(arch_specific 'shortcuts' $manifest $arch)
64 | $shortcuts | Where-Object { $_ -ne $null } | ForEach-Object {
65 | $name = $_.item(1)
66 | $shortcut = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath("$(shortcut_folder $global)\$name.lnk")
67 | Write-Host "Removing shortcut $(friendly_path $shortcut)"
68 | if (Test-Path -Path $shortcut) {
69 | Remove-Item $shortcut
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/lib/system.ps1:
--------------------------------------------------------------------------------
1 | # System-related functions
2 |
3 | ## Environment Variables
4 |
5 | function Publish-EnvVar {
6 | if (-not ('Win32.NativeMethods' -as [Type])) {
7 | Add-Type -Namespace Win32 -Name NativeMethods -MemberDefinition @'
8 | [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
9 | public static extern IntPtr SendMessageTimeout(
10 | IntPtr hWnd, uint Msg, UIntPtr wParam, string lParam,
11 | uint fuFlags, uint uTimeout, out UIntPtr lpdwResult
12 | );
13 | '@
14 | }
15 |
16 | $HWND_BROADCAST = [IntPtr] 0xffff
17 | $WM_SETTINGCHANGE = 0x1a
18 | $result = [UIntPtr]::Zero
19 |
20 | [Win32.NativeMethods]::SendMessageTimeout($HWND_BROADCAST,
21 | $WM_SETTINGCHANGE,
22 | [UIntPtr]::Zero,
23 | 'Environment',
24 | 2,
25 | 5000,
26 | [ref] $result
27 | ) | Out-Null
28 | }
29 |
30 | function Get-EnvVar {
31 | param(
32 | [string]$Name,
33 | [switch]$Global
34 | )
35 |
36 | $registerKey = if ($Global) {
37 | Get-Item -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager'
38 | } else {
39 | Get-Item -Path 'HKCU:'
40 | }
41 | $envRegisterKey = $registerKey.OpenSubKey('Environment')
42 | $registryValueOption = [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames
43 | $envRegisterKey.GetValue($Name, $null, $registryValueOption)
44 | }
45 |
46 | function Set-EnvVar {
47 | param(
48 | [string]$Name,
49 | [string]$Value,
50 | [switch]$Global
51 | )
52 |
53 | $registerKey = if ($Global) {
54 | Get-Item -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager'
55 | } else {
56 | Get-Item -Path 'HKCU:'
57 | }
58 | $envRegisterKey = $registerKey.OpenSubKey('Environment', $true)
59 | if ($null -eq $Value -or $Value -eq '') {
60 | if ($envRegisterKey.GetValue($Name)) {
61 | $envRegisterKey.DeleteValue($Name)
62 | }
63 | } else {
64 | $registryValueKind = if ($Value.Contains('%')) {
65 | [Microsoft.Win32.RegistryValueKind]::ExpandString
66 | } elseif ($envRegisterKey.GetValue($Name)) {
67 | $envRegisterKey.GetValueKind($Name)
68 | } else {
69 | [Microsoft.Win32.RegistryValueKind]::String
70 | }
71 | $envRegisterKey.SetValue($Name, $Value, $registryValueKind)
72 | }
73 | Publish-EnvVar
74 | }
75 |
76 | function Split-PathLikeEnvVar {
77 | param(
78 | [string[]]$Pattern,
79 | [string]$Path
80 | )
81 |
82 | if ($null -eq $Path -and $Path -eq '') {
83 | return $null, $null
84 | } else {
85 | $splitPattern = $Pattern.Split(';', [System.StringSplitOptions]::RemoveEmptyEntries)
86 | $splitPath = $Path.Split(';', [System.StringSplitOptions]::RemoveEmptyEntries)
87 | $inPath = @()
88 | foreach ($p in $splitPattern) {
89 | $inPath += $splitPath.Where({ $_ -like $p })
90 | $splitPath = $splitPath.Where({ $_ -notlike $p })
91 | }
92 | return ($inPath -join ';'), ($splitPath -join ';')
93 | }
94 | }
95 |
96 | function Add-Path {
97 | param(
98 | [string[]]$Path,
99 | [string]$TargetEnvVar = 'PATH',
100 | [switch]$Global,
101 | [switch]$Force,
102 | [switch]$Quiet
103 | )
104 |
105 | # future sessions
106 | $inPath, $strippedPath = Split-PathLikeEnvVar $Path (Get-EnvVar -Name $TargetEnvVar -Global:$Global)
107 | if (!$inPath -or $Force) {
108 | if (!$Quiet) {
109 | $Path | ForEach-Object {
110 | Write-Host "Adding $(friendly_path $_) to $(if ($Global) {'global'} else {'your'}) path."
111 | }
112 | }
113 | Set-EnvVar -Name $TargetEnvVar -Value ((@($Path) + $strippedPath) -join ';') -Global:$Global
114 | }
115 | # current session
116 | $inPath, $strippedPath = Split-PathLikeEnvVar $Path $env:PATH
117 | if (!$inPath -or $Force) {
118 | $env:PATH = (@($Path) + $strippedPath) -join ';'
119 | }
120 | }
121 |
122 | function Remove-Path {
123 | param(
124 | [string[]]$Path,
125 | [string]$TargetEnvVar = 'PATH',
126 | [switch]$Global,
127 | [switch]$Quiet,
128 | [switch]$PassThru
129 | )
130 |
131 | # future sessions
132 | $inPath, $strippedPath = Split-PathLikeEnvVar $Path (Get-EnvVar -Name $TargetEnvVar -Global:$Global)
133 | if ($inPath) {
134 | if (!$Quiet) {
135 | $Path | ForEach-Object {
136 | Write-Host "Removing $(friendly_path $_) from $(if ($Global) {'global'} else {'your'}) path."
137 | }
138 | }
139 | Set-EnvVar -Name $TargetEnvVar -Value $strippedPath -Global:$Global
140 | }
141 | # current session
142 | $inSessionPath, $strippedPath = Split-PathLikeEnvVar $Path $env:PATH
143 | if ($inSessionPath) {
144 | $env:PATH = $strippedPath
145 | }
146 | if ($PassThru) {
147 | return $inPath
148 | }
149 | }
150 |
151 | ## Deprecated functions
152 |
153 | function env($name, $global, $val) {
154 | if ($PSBoundParameters.ContainsKey('val')) {
155 | Show-DeprecatedWarning $MyInvocation 'Set-EnvVar'
156 | Set-EnvVar -Name $name -Value $val -Global:$global
157 | } else {
158 | Show-DeprecatedWarning $MyInvocation 'Get-EnvVar'
159 | Get-EnvVar -Name $name -Global:$global
160 | }
161 | }
162 |
163 | function strip_path($orig_path, $dir) {
164 | Show-DeprecatedWarning $MyInvocation 'Split-PathLikeEnvVar'
165 | Split-PathLikeEnvVar -Pattern @($dir) -Path $orig_path
166 | }
167 |
168 | function add_first_in_path($dir, $global) {
169 | Show-DeprecatedWarning $MyInvocation 'Add-Path'
170 | Add-Path -Path $dir -Global:$global -Force
171 | }
172 |
173 | function remove_from_path($dir, $global) {
174 | Show-DeprecatedWarning $MyInvocation 'Remove-Path'
175 | Remove-Path -Path $dir -Global:$global
176 | }
177 |
--------------------------------------------------------------------------------
/libexec/scoop-alias.ps1:
--------------------------------------------------------------------------------
1 | # Usage: scoop alias [options] []
2 | # Summary: Manage scoop aliases
3 | # Help: Available subcommands: add, rm, list.
4 | #
5 | # Aliases are custom Scoop subcommands that can be created to make common tasks easier.
6 | #
7 | # To add an alias:
8 | #
9 | # scoop alias add []
10 | #
11 | # e.g.,
12 | #
13 | # scoop alias add rm 'scoop uninstall $args[0]' 'Uninstall an app'
14 | # scoop alias add upgrade 'scoop update *' 'Update all apps, just like "brew" or "apt"'
15 | #
16 | # To remove an alias:
17 | #
18 | # scoop alias rm
19 | #
20 | # To list all aliases:
21 | #
22 | # scoop alias list [-v|--verbose]
23 | #
24 | # Options:
25 | # -v, --verbose Show alias description and table headers (works only for "list")
26 |
27 | param($SubCommand)
28 |
29 | . "$PSScriptRoot\..\lib\getopt.ps1"
30 |
31 | $SubCommands = @('add', 'rm', 'list')
32 | if ($SubCommand -notin $SubCommands) {
33 | if (!$SubCommand) {
34 | error ' missing'
35 | } else {
36 | error "'$SubCommand' is not one of available subcommands: $($SubCommands -join ', ')"
37 | }
38 | my_usage
39 | exit 1
40 | }
41 |
42 | $opt, $other, $err = getopt $Args 'v' 'verbose'
43 | if ($err) { "scoop alias: $err"; exit 1 }
44 |
45 | $name, $command, $description = $other
46 | $verbose = $opt.v -or $opt.verbose
47 |
48 | switch ($SubCommand) {
49 | 'add' {
50 | if (!$name -or !$command) {
51 | error " and must be specified for subcommand 'add'"
52 | exit 1
53 | }
54 | add_alias $name $command $description
55 | }
56 | 'rm' {
57 | if (!$name) {
58 | error " must be specified for subcommand 'rm'"
59 | exit 1
60 | }
61 | rm_alias $name
62 | }
63 | 'list' {
64 | list_aliases $verbose
65 | }
66 | }
67 |
68 | exit 0
69 |
--------------------------------------------------------------------------------
/libexec/scoop-bucket.ps1:
--------------------------------------------------------------------------------
1 | # Usage: scoop bucket add|list|known|rm []
2 | # Summary: Manage Scoop buckets
3 | # Help: Add, list or remove buckets.
4 | #
5 | # Buckets are repositories of apps available to install. Scoop comes with
6 | # a default bucket, but you can also add buckets that you or others have
7 | # published.
8 | #
9 | # To add a bucket:
10 | # scoop bucket add []
11 | #
12 | # e.g.:
13 | # scoop bucket add extras https://github.com/ScoopInstaller/Extras.git
14 | #
15 | # Since the 'extras' bucket is known to Scoop, this can be shortened to:
16 | # scoop bucket add extras
17 | #
18 | # To list all known buckets, use:
19 | # scoop bucket known
20 | param($cmd, $name, $repo)
21 |
22 | if (get_config USE_SQLITE_CACHE) {
23 | . "$PSScriptRoot\..\lib\manifest.ps1"
24 | . "$PSScriptRoot\..\lib\database.ps1"
25 | }
26 |
27 | $usage_add = 'usage: scoop bucket add []'
28 | $usage_rm = 'usage: scoop bucket rm '
29 |
30 | switch ($cmd) {
31 | 'add' {
32 | if (!$name) {
33 | ' missing'
34 | $usage_add
35 | exit 1
36 | }
37 | if (!$repo) {
38 | $repo = known_bucket_repo $name
39 | if (!$repo) {
40 | "Unknown bucket '$name'. Try specifying ."
41 | $usage_add
42 | exit 1
43 | }
44 | }
45 | $status = add_bucket $name $repo
46 | exit $status
47 | }
48 | 'rm' {
49 | if (!$name) {
50 | ' missing'
51 | $usage_rm
52 | exit 1
53 | }
54 | $status = rm_bucket $name
55 | exit $status
56 | }
57 | 'list' {
58 | $buckets = list_buckets
59 | if (!$buckets.Length) {
60 | warn "No bucket found. Please run 'scoop bucket add main' to add the default 'main' bucket."
61 | exit 2
62 | } else {
63 | $buckets
64 | exit 0
65 | }
66 | }
67 | 'known' {
68 | known_buckets
69 | exit 0
70 | }
71 | default {
72 | "scoop bucket: cmd '$cmd' not supported"
73 | my_usage
74 | exit 1
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/libexec/scoop-cache.ps1:
--------------------------------------------------------------------------------
1 | # Usage: scoop cache show|rm [app(s)]
2 | # Summary: Show or clear the download cache
3 | # Help: Scoop caches downloads so you don't need to download the same files
4 | # when you uninstall and re-install the same version of an app.
5 | #
6 | # You can use
7 | # scoop cache show
8 | # to see what's in the cache, and
9 | # scoop cache rm to remove downloads for a specific app.
10 | #
11 | # To clear everything in your cache, use:
12 | # scoop cache rm *
13 | # You can also use the `-a/--all` switch in place of `*` here
14 |
15 | param($cmd)
16 |
17 | function cacheinfo($file) {
18 | $app, $version, $url = $file.Name -split '#'
19 | New-Object PSObject -Property @{ Name = $app; Version = $version; Length = $file.Length }
20 | }
21 |
22 | function cacheshow($app) {
23 | if (!$app -or $app -eq '*') {
24 | $app = '.*?'
25 | } else {
26 | $app = '(' + ($app -join '|') + ')'
27 | }
28 | $files = @(Get-ChildItem $cachedir | Where-Object -Property Name -Value "^$app#" -Match)
29 | $totalLength = ($files | Measure-Object -Property Length -Sum).Sum
30 |
31 | $files | ForEach-Object { cacheinfo $_ } | Select-Object Name, Version, Length
32 |
33 | Write-Host "Total: $($files.Length) $(pluralize $files.Length 'file' 'files'), $(filesize $totalLength)" -ForegroundColor Yellow
34 | }
35 |
36 | function cacheremove($app) {
37 | if (!$app) {
38 | 'ERROR: missing'
39 | my_usage
40 | exit 1
41 | } elseif ($app -eq '*' -or $app -eq '-a' -or $app -eq '--all') {
42 | $files = @(Get-ChildItem $cachedir)
43 | } else {
44 | $app = '(' + ($app -join '|') + ')'
45 | $files = @(Get-ChildItem $cachedir | Where-Object -Property Name -Value "^$app#" -Match)
46 | }
47 | $totalLength = ($files | Measure-Object -Property Length -Sum).Sum
48 |
49 | $files | ForEach-Object {
50 | $curr = cacheinfo $_
51 | Write-Host "Removing $($_.Name)..."
52 | Remove-Item $_.FullName
53 | if(Test-Path "$cachedir\$($curr.Name).txt") {
54 | Remove-Item "$cachedir\$($curr.Name).txt"
55 | }
56 | }
57 |
58 | Write-Host "Deleted: $($files.Length) $(pluralize $files.Length 'file' 'files'), $(filesize $totalLength)" -ForegroundColor Yellow
59 | }
60 |
61 | switch($cmd) {
62 | 'rm' {
63 | cacheremove $Args
64 | }
65 | 'show' {
66 | cacheshow $Args
67 | }
68 | default {
69 | cacheshow (@($cmd) + $Args)
70 | }
71 | }
72 |
73 | exit 0
74 |
--------------------------------------------------------------------------------
/libexec/scoop-cat.ps1:
--------------------------------------------------------------------------------
1 | # Usage: scoop cat
2 | # Summary: Show content of specified manifest.
3 | # Help: Show content of specified manifest.
4 | # If configured, `bat` will be used to pretty-print the JSON.
5 | # See `cat_style` in `scoop help config` for further information.
6 |
7 | param($app)
8 |
9 | . "$PSScriptRoot\..\lib\json.ps1" # 'ConvertToPrettyJson'
10 | . "$PSScriptRoot\..\lib\manifest.ps1" # 'Get-Manifest'
11 |
12 | if (!$app) { error ' missing'; my_usage; exit 1 }
13 |
14 | $null, $manifest, $bucket, $url = Get-Manifest $app
15 |
16 | if ($manifest) {
17 | $style = get_config CAT_STYLE
18 | if ($style) {
19 | $manifest | ConvertToPrettyJson | bat --no-paging --style $style --language json
20 | } else {
21 | $manifest | ConvertToPrettyJson
22 | }
23 | } else {
24 | abort "Couldn't find manifest for '$app'$(if($bucket) { " from '$bucket' bucket" } elseif($url) { " at '$url'" })."
25 | }
26 |
27 | exit $exitCode
28 |
--------------------------------------------------------------------------------
/libexec/scoop-checkup.ps1:
--------------------------------------------------------------------------------
1 | # Usage: scoop checkup
2 | # Summary: Check for potential problems
3 | # Help: Performs a series of diagnostic tests to try to identify things that may
4 | # cause problems with Scoop.
5 |
6 | . "$PSScriptRoot\..\lib\diagnostic.ps1"
7 |
8 | $issues = 0
9 | $defenderIssues = 0
10 |
11 | $adminPrivileges = ([System.Security.Principal.WindowsPrincipal] [System.Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)
12 |
13 | if ($adminPrivileges -and $env:USERNAME -ne 'WDAGUtilityAccount') {
14 | $defenderIssues += !(check_windows_defender $false)
15 | $defenderIssues += !(check_windows_defender $true)
16 | }
17 |
18 | $issues += !(check_main_bucket)
19 | $issues += !(check_long_paths)
20 | $issues += !(Get-WindowsDeveloperModeStatus)
21 |
22 | if (!(Test-HelperInstalled -Helper 7zip) -and !(get_config USE_EXTERNAL_7ZIP)) {
23 | warn "'7-Zip' is not installed! It's required for unpacking most programs. Please Run 'scoop install 7zip'."
24 | $issues++
25 | }
26 |
27 | if (!(Test-HelperInstalled -Helper Innounp)) {
28 | warn "'Inno Setup Unpacker' is not installed! It's required for unpacking InnoSetup files. Please run 'scoop install innounp'."
29 | $issues++
30 | }
31 |
32 | if (!(Test-HelperInstalled -Helper Dark)) {
33 | 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'."
34 | $issues++
35 | }
36 |
37 | $globaldir = New-Object System.IO.DriveInfo($globaldir)
38 | if ($globaldir.DriveFormat -ne 'NTFS') {
39 | error "Scoop requires an NTFS volume to work! Please point `$env:SCOOP_GLOBAL or 'global_path' variable in '~/.config/scoop/config.json' to another Drive."
40 | $issues++
41 | }
42 |
43 | $scoopdir = New-Object System.IO.DriveInfo($scoopdir)
44 | if ($scoopdir.DriveFormat -ne 'NTFS') {
45 | error "Scoop requires an NTFS volume to work! Please point `$env:SCOOP or 'root_path' variable in '~/.config/scoop/config.json' to another Drive."
46 | $issues++
47 | }
48 |
49 | if ($issues) {
50 | warn "Found $issues potential $(pluralize $issues problem problems)."
51 | } elseif ($defenderIssues) {
52 | info "Found $defenderIssues performance $(pluralize $defenderIssues problem problems)."
53 | warn "Security is more important than performance, in most cases."
54 | } else {
55 | success "No problems identified!"
56 | }
57 |
58 | exit 0
59 |
--------------------------------------------------------------------------------
/libexec/scoop-cleanup.ps1:
--------------------------------------------------------------------------------
1 | # Usage: scoop cleanup [options]
2 | # Summary: Cleanup apps by removing old versions
3 | # Help: 'scoop cleanup' cleans Scoop apps by removing old versions.
4 | # 'scoop cleanup ' cleans up the old versions of that app if said versions exist.
5 | #
6 | # You can use '*' in place of or `-a`/`--all` switch to cleanup all apps.
7 | #
8 | # Options:
9 | # -a, --all Cleanup all apps (alternative to '*')
10 | # -g, --global Cleanup a globally installed app
11 | # -k, --cache Remove outdated download cache
12 |
13 | . "$PSScriptRoot\..\lib\getopt.ps1"
14 | . "$PSScriptRoot\..\lib\manifest.ps1" # 'Select-CurrentVersion' (indirectly)
15 | . "$PSScriptRoot\..\lib\versions.ps1" # 'Select-CurrentVersion'
16 | . "$PSScriptRoot\..\lib\install.ps1" # persist related
17 |
18 | $opt, $apps, $err = getopt $args 'agk' 'all', 'global', 'cache'
19 | if ($err) { "scoop cleanup: $err"; exit 1 }
20 | $global = $opt.g -or $opt.global
21 | $cache = $opt.k -or $opt.cache
22 | $all = $opt.a -or $opt.all
23 |
24 | if (!$apps -and !$all) { 'ERROR: missing'; my_usage; exit 1 }
25 |
26 | if ($global -and !(is_admin)) {
27 | 'ERROR: you need admin rights to cleanup global apps'; exit 1
28 | }
29 |
30 | function cleanup($app, $global, $verbose, $cache) {
31 | $current_version = Select-CurrentVersion -AppName $app -Global:$global
32 | if ($cache) {
33 | Remove-Item "$cachedir\$app#*" -Exclude "$app#$current_version#*"
34 | }
35 | $appDir = appdir $app $global
36 | $versions = Get-ChildItem $appDir -Name
37 | $versions = $versions | Where-Object { $current_version -ne $_ -and $_ -ne 'current' }
38 | if (!$versions) {
39 | if ($verbose) { success "$app is already clean" }
40 | return
41 | }
42 |
43 | Write-Host -f yellow "Removing $app`:" -NoNewline
44 | $versions | ForEach-Object {
45 | $version = $_
46 | Write-Host " $version" -NoNewline
47 | $dir = versiondir $app $version $global
48 | # unlink all potential old link before doing recursive Remove-Item
49 | unlink_persist_data (installed_manifest $app $version $global) $dir
50 | Remove-Item $dir -ErrorAction Stop -Recurse -Force
51 | }
52 | $leftVersions = Get-ChildItem $appDir
53 | if ($leftVersions.Length -eq 1 -and $leftVersions.Name -eq 'current' -and $leftVersions.LinkType) {
54 | attrib $leftVersions.FullName -R /L
55 | Remove-Item $leftVersions.FullName -ErrorAction Stop -Force
56 | $leftVersions = $null
57 | }
58 | if (!$leftVersions) {
59 | Remove-Item $appDir -ErrorAction Stop -Force
60 | }
61 | Write-Host ''
62 | }
63 |
64 | if ($apps -or $all) {
65 | if ($apps -eq '*' -or $all) {
66 | $verbose = $false
67 | $apps = applist (installed_apps $false) $false
68 | if ($global) {
69 | $apps += applist (installed_apps $true) $true
70 | }
71 | } else {
72 | $verbose = $true
73 | $apps = Confirm-InstallationStatus $apps -Global:$global
74 | }
75 |
76 | # $apps is now a list of ($app, $global) tuples
77 | $apps | ForEach-Object { cleanup @_ $verbose $cache }
78 |
79 | if ($cache) {
80 | Remove-Item "$cachedir\*.download" -ErrorAction Ignore
81 | }
82 |
83 | if (!$verbose) {
84 | success 'Everything is shiny now!'
85 | }
86 | }
87 |
88 | exit 0
89 |
--------------------------------------------------------------------------------
/libexec/scoop-create.ps1:
--------------------------------------------------------------------------------
1 | # Usage: scoop create
2 | # Summary: Create a custom app manifest
3 | # Help: Create your own custom app manifest
4 | param($url)
5 |
6 | function create_manifest($url) {
7 | $manifest = new_manifest
8 |
9 | $manifest.url = $url
10 |
11 | $url_parts = $null
12 | try {
13 | $url_parts = parse_url $url
14 | } catch {
15 | abort "Error: $url is not a valid URL"
16 | }
17 |
18 | $name = choose_item $url_parts 'App name'
19 | $name = if ($name.Length -gt 0) {
20 | $name
21 | } else {
22 | file_name ($url_parts | Select-Object -Last 1)
23 | }
24 |
25 | $manifest.version = choose_item $url_parts 'Version'
26 |
27 | $manifest | ConvertTo-Json | Out-File -FilePath "$name.json" -Encoding ASCII
28 | $manifest_path = Join-Path $pwd "$name.json"
29 | Write-Host "Created '$manifest_path'."
30 | }
31 |
32 | function new_manifest() {
33 | @{ 'homepage' = ''; 'license' = ''; 'version' = ''; 'url' = '';
34 | 'hash' = ''; 'extract_dir' = ''; 'bin' = ''; 'depends' = ''
35 | }
36 | }
37 |
38 | function file_name($segment) {
39 | $segment.substring(0, $segment.lastindexof('.'))
40 | }
41 |
42 | function parse_url($url) {
43 | $uri = New-Object Uri $url
44 | $uri.pathandquery.substring(1).split('/')
45 | }
46 |
47 | function choose_item($list, $query) {
48 | for ($i = 0; $i -lt $list.count; $i++) {
49 | $item = $list[$i]
50 | Write-Host "$($i + 1)) $item"
51 | }
52 | $sel = Read-Host $query
53 |
54 | if ($sel.trim() -match '^[0-9+]$') {
55 | return $list[$sel - 1]
56 | }
57 |
58 | $sel
59 | }
60 |
61 | if (!$url) {
62 | & "$PSScriptRoot\scoop-help.ps1" create
63 | } else {
64 | create_manifest $url
65 | }
66 |
67 | exit 0
68 |
--------------------------------------------------------------------------------
/libexec/scoop-depends.ps1:
--------------------------------------------------------------------------------
1 | # Usage: scoop depends
2 | # Summary: List dependencies for an app, in the order they'll be installed
3 |
4 | . "$PSScriptRoot\..\lib\getopt.ps1"
5 | . "$PSScriptRoot\..\lib\depends.ps1" # 'Get-Dependency'
6 | . "$PSScriptRoot\..\lib\manifest.ps1" # 'Get-Manifest' (indirectly)
7 |
8 | $opt, $apps, $err = getopt $args 'a:' 'arch='
9 | $app = $apps[0]
10 |
11 | if(!$app) { error ' missing'; my_usage; exit 1 }
12 |
13 | $architecture = Get-DefaultArchitecture
14 | try {
15 | $architecture = Format-ArchitectureString ($opt.a + $opt.arch)
16 | } catch {
17 | abort "ERROR: $_"
18 | }
19 |
20 | $deps = @()
21 | Get-Dependency $app $architecture | ForEach-Object {
22 | $dep = [ordered]@{}
23 | $dep.Source, $dep.Name = $_ -split '/'
24 | $deps += [PSCustomObject]$dep
25 | }
26 | $deps
27 |
28 | exit 0
29 |
--------------------------------------------------------------------------------
/libexec/scoop-download.ps1:
--------------------------------------------------------------------------------
1 | # Usage: scoop download [options]
2 | # Summary: Download apps in the cache folder and verify hashes
3 | # Help: e.g. The usual way to download an app, without installing it (uses your local 'buckets'):
4 | # scoop download git
5 | #
6 | # To download a different version of the app
7 | # (note that this will auto-generate the manifest using current version):
8 | # scoop download gh@2.7.0
9 | #
10 | # To download an app from a manifest at a URL:
11 | # scoop download https://raw.githubusercontent.com/ScoopInstaller/Main/master/bucket/runat.json
12 | #
13 | # To download an app from a manifest on your computer
14 | # scoop download path\to\app.json
15 | #
16 | # Options:
17 | # -f, --force Force download (overwrite cache)
18 | # -s, --skip-hash-check Skip hash verification (use with caution!)
19 | # -u, --no-update-scoop Don't update Scoop before downloading if it's outdated
20 | # -a, --arch <32bit|64bit|arm64> Use the specified architecture, if the app supports it
21 |
22 | . "$PSScriptRoot\..\lib\getopt.ps1"
23 | . "$PSScriptRoot\..\lib\json.ps1" # 'autoupdate.ps1' (indirectly)
24 | . "$PSScriptRoot\..\lib\autoupdate.ps1" # 'generate_user_manifest' (indirectly)
25 | . "$PSScriptRoot\..\lib\manifest.ps1" # 'generate_user_manifest' 'Get-Manifest'
26 | . "$PSScriptRoot\..\lib\install.ps1"
27 | if (get_config USE_SQLITE_CACHE) {
28 | . "$PSScriptRoot\..\lib\database.ps1"
29 | }
30 |
31 | $opt, $apps, $err = getopt $args 'fsua:' 'force', 'skip-hash-check', 'no-update-scoop', 'arch='
32 | if ($err) { error "scoop download: $err"; exit 1 }
33 |
34 | $check_hash = !($opt.s -or $opt.'skip-hash-check')
35 | $use_cache = !($opt.f -or $opt.force)
36 | $architecture = Get-DefaultArchitecture
37 | try {
38 | $architecture = Format-ArchitectureString ($opt.a + $opt.arch)
39 | } catch {
40 | abort "ERROR: $_"
41 | }
42 |
43 | if (!$apps) { error ' missing'; my_usage; exit 1 }
44 |
45 | if (is_scoop_outdated) {
46 | if ($opt.u -or $opt.'no-update-scoop') {
47 | warn "Scoop is out of date."
48 | } else {
49 | & "$PSScriptRoot\scoop-update.ps1"
50 | }
51 | }
52 |
53 | # we only want to show this warning once
54 | if(!$use_cache) { warn "Cache is being ignored." }
55 |
56 | foreach ($curr_app in $apps) {
57 | # Prevent leaking variables from previous iteration
58 | $bucket = $version = $app = $manifest = $url = $null
59 |
60 | $app, $bucket, $version = parse_app $curr_app
61 | $app, $manifest, $bucket, $url = Get-Manifest "$bucket/$app"
62 |
63 | info "Downloading '$app'$(if ($version) { " ($version)" }) [$architecture]$(if ($bucket) { " from $bucket bucket" })"
64 |
65 | # Generate manifest if there is different version in manifest
66 | if (($null -ne $version) -and ($manifest.version -ne $version)) {
67 | $generated = generate_user_manifest $app $bucket $version
68 | if ($null -eq $generated) {
69 | error 'Manifest cannot be generated with provided version'
70 | continue
71 | }
72 | $manifest = parse_json($generated)
73 | }
74 |
75 | if(!$manifest) {
76 | error "Couldn't find manifest for '$app'$(if($bucket) { " from '$bucket' bucket" } elseif($url) { " at '$url'" })."
77 | continue
78 | }
79 | $version = $manifest.version
80 | if(!$version) {
81 | error "Manifest doesn't specify a version."
82 | continue
83 | }
84 | if($version -match '[^\w\.\-\+_]') {
85 | error "Manifest version has unsupported character '$($matches[0])'."
86 | continue
87 | }
88 |
89 | $curr_check_hash = $check_hash
90 | if ($version -eq 'nightly') {
91 | $version = nightly_version
92 | $curr_check_hash = $false
93 | }
94 |
95 | $architecture = Get-SupportedArchitecture $manifest $architecture
96 | if ($null -eq $architecture) {
97 | error "'$app' doesn't support current architecture!"
98 | continue
99 | }
100 |
101 | if(Test-Aria2Enabled) {
102 | Invoke-CachedAria2Download $app $version $manifest $architecture $cachedir $manifest.cookie $use_cache $curr_check_hash
103 | } else {
104 | foreach($url in script:url $manifest $architecture) {
105 | try {
106 | Invoke-CachedDownload $app $version $url $null $manifest.cookie $use_cache
107 | } catch {
108 | write-host -f darkred $_
109 | error "URL $url is not valid"
110 | $dl_failure = $true
111 | continue
112 | }
113 |
114 | if($curr_check_hash) {
115 | $manifest_hash = hash_for_url $manifest $url $architecture
116 | $cached = cache_path $app $version $url
117 | $ok, $err = check_hash $cached $manifest_hash (show_app $app $bucket)
118 |
119 | if(!$ok) {
120 | error $err
121 | if(test-path $cached) {
122 | # rm cached file
123 | Remove-Item -force $cached
124 | }
125 | if ($url -like '*sourceforge.net*') {
126 | warn 'SourceForge.net is known for causing hash validation fails. Please try again before opening a ticket.'
127 | }
128 | error (new_issue_msg $app $bucket "hash check failed")
129 | continue
130 | }
131 | } else {
132 | info "Skipping hash verification."
133 | }
134 | }
135 | }
136 |
137 | if (!$dl_failure) {
138 | success "'$app' ($version) was downloaded successfully!"
139 | }
140 | }
141 |
142 | exit 0
143 |
--------------------------------------------------------------------------------
/libexec/scoop-export.ps1:
--------------------------------------------------------------------------------
1 | # Usage: scoop export > scoopfile.json
2 | # Summary: Exports installed apps, buckets (and optionally configs) in JSON format
3 | # Help: Options:
4 | # -c, --config Export the Scoop configuration file too
5 |
6 | . "$PSScriptRoot\..\lib\json.ps1" # 'ConvertToPrettyJson'
7 |
8 | $export = @{}
9 |
10 | if ($args[0] -eq '-c' -or $args[0] -eq '--config') {
11 | $export.config = $scoopConfig
12 | # Remove machine-specific properties
13 | foreach ($prop in 'last_update', 'root_path', 'global_path', 'cache_path', 'alias') {
14 | $export.config.PSObject.Properties.Remove($prop)
15 | }
16 | }
17 |
18 | $export.buckets = list_buckets
19 | $export.apps = @(& "$PSScriptRoot\scoop-list.ps1" 6>$null)
20 |
21 | $export | ConvertToPrettyJSON
22 |
23 | exit 0
24 |
--------------------------------------------------------------------------------
/libexec/scoop-help.ps1:
--------------------------------------------------------------------------------
1 | # Usage: scoop help
2 | # Summary: Show help for a command
3 | param($cmd)
4 |
5 | function print_help($cmd) {
6 | $file = Get-Content (command_path $cmd) -Raw
7 |
8 | $usage = usage $file
9 | $help = scoop_help $file
10 |
11 | if ($usage) { "$usage`n" }
12 | if ($help) { $help }
13 | }
14 |
15 | function print_summaries {
16 | $commands = @()
17 |
18 | command_files | ForEach-Object {
19 | $command = [ordered]@{}
20 | $command.Command = command_name $_
21 | $command.Summary = summary (Get-Content (command_path $command.Command))
22 | $commands += [PSCustomObject]$command
23 | }
24 |
25 | $commands
26 | }
27 |
28 | $commands = commands
29 |
30 | if(!($cmd)) {
31 | Write-Host "Usage: scoop []
32 |
33 | Available commands are listed below.
34 |
35 | Type 'scoop help ' to get more help for a specific command."
36 | print_summaries
37 | } elseif($commands -contains $cmd) {
38 | print_help $cmd
39 | } else {
40 | warn "scoop help: no such command '$cmd'"
41 | exit 1
42 | }
43 |
44 | exit 0
45 |
--------------------------------------------------------------------------------
/libexec/scoop-hold.ps1:
--------------------------------------------------------------------------------
1 | # Usage: scoop hold
2 | # Summary: Hold an app to disable updates
3 | # Help: To hold a user-scoped app:
4 | # scoop hold
5 | #
6 | # To hold a global app:
7 | # scoop hold -g
8 | #
9 | # Options:
10 | # -g, --global Hold globally installed apps
11 |
12 | . "$PSScriptRoot\..\lib\getopt.ps1"
13 | . "$PSScriptRoot\..\lib\json.ps1" # 'save_install_info' (indirectly)
14 | . "$PSScriptRoot\..\lib\manifest.ps1" # 'install_info' 'Select-CurrentVersion' (indirectly)
15 | . "$PSScriptRoot\..\lib\versions.ps1" # 'Select-CurrentVersion'
16 |
17 | $opt, $apps, $err = getopt $args 'g' 'global'
18 | if ($err) { "scoop hold: $err"; exit 1 }
19 |
20 | $global = $opt.g -or $opt.global
21 |
22 | if (!$apps) {
23 | my_usage
24 | exit 1
25 | }
26 |
27 | if ($global -and !(is_admin)) {
28 | error 'You need admin rights to hold a global app.'
29 | exit 1
30 | }
31 |
32 | foreach ($app in $apps) {
33 |
34 | if ($app -eq 'scoop') {
35 | $hold_update_until = [System.DateTime]::Now.AddDays(1)
36 | set_config HOLD_UPDATE_UNTIL $hold_update_until.ToString('o') | Out-Null
37 | success "$app is now held and might not be updated until $($hold_update_until.ToLocalTime())."
38 | continue
39 | }
40 | if (!(installed $app $global)) {
41 | if ($global) {
42 | error "'$app' is not installed globally."
43 | } else {
44 | error "'$app' is not installed."
45 | }
46 | continue
47 | }
48 |
49 | if (get_config NO_JUNCTION) {
50 | $version = Select-CurrentVersion -App $app -Global:$global
51 | } else {
52 | $version = 'current'
53 | }
54 | $dir = versiondir $app $version $global
55 | $json = install_info $app $version $global
56 | if (!$json) {
57 | error "Failed to hold '$app'."
58 | continue
59 | }
60 | $install = @{}
61 | $json | Get-Member -MemberType Properties | ForEach-Object { $install.Add($_.Name, $json.($_.Name)) }
62 | if ($install.hold) {
63 | info "'$app' is already held."
64 | continue
65 | }
66 | $install.hold = $true
67 | save_install_info $install $dir
68 | success "$app is now held and can not be updated anymore."
69 | }
70 |
71 | exit $exitcode
72 |
--------------------------------------------------------------------------------
/libexec/scoop-home.ps1:
--------------------------------------------------------------------------------
1 | # Usage: scoop home
2 | # Summary: Opens the app homepage
3 | param($app)
4 |
5 | . "$PSScriptRoot\..\lib\manifest.ps1" # 'Get-Manifest'
6 |
7 | if ($app) {
8 | $null, $manifest, $bucket, $null = Get-Manifest $app
9 | if ($manifest) {
10 | if ($manifest.homepage) {
11 | Start-Process $manifest.homepage
12 | } else {
13 | abort "Could not find homepage in manifest for '$app'."
14 | }
15 | } else {
16 | abort "Could not find manifest for '$app'."
17 | }
18 | } else {
19 | my_usage
20 | exit 1
21 | }
22 |
23 | exit 0
24 |
--------------------------------------------------------------------------------
/libexec/scoop-import.ps1:
--------------------------------------------------------------------------------
1 | # Usage: scoop import
2 | # Summary: Imports apps, buckets and configs from a Scoopfile in JSON format
3 | # Help: To replicate a Scoop installation from a file stored on Desktop, run
4 | # scoop import Desktop\scoopfile.json
5 |
6 | param(
7 | [Parameter(Mandatory)]
8 | [String]
9 | $scoopfile
10 | )
11 |
12 | . "$PSScriptRoot\..\lib\manifest.ps1"
13 |
14 | $import = $null
15 | $bucket_names = @()
16 | $def_arch = Get-DefaultArchitecture
17 |
18 | if (Test-Path $scoopfile) {
19 | $import = parse_json $scoopfile
20 | } elseif ($scoopfile -match '^(ht|f)tps?://|\\\\') {
21 | $import = url_manifest $scoopfile
22 | }
23 |
24 | if (!$import) { abort 'Input file not a valid JSON.' }
25 |
26 | foreach ($item in $import.config.PSObject.Properties) {
27 | set_config $item.Name $item.Value | Out-Null
28 | Write-Host "'$($item.Name)' has been set to '$($item.Value)'"
29 | }
30 |
31 | foreach ($item in $import.buckets) {
32 | add_bucket $item.Name $item.Source | Out-Null
33 | $bucket_names += $item.Name
34 | }
35 |
36 | foreach ($item in $import.apps) {
37 | $instArgs = @()
38 | $holdArgs = @()
39 | $info = $item.Info -Split ', '
40 | if ('Global install' -in $info) {
41 | $instArgs += '--global'
42 | $holdArgs += '--global'
43 | }
44 | if ('64bit' -in $info -and '64bit' -ne $def_arch) {
45 | $instArgs += '--arch', '64bit'
46 | } elseif ('32bit' -in $info -and '32bit' -ne $def_arch) {
47 | $instArgs += '--arch', '32bit'
48 | } elseif ('arm64' -in $info -and 'arm64' -ne $def_arch) {
49 | $instArgs += '--arch', 'arm64'
50 | }
51 |
52 | $app = if ($item.Source -in $bucket_names) {
53 | "$($item.Source)/$($item.Name)"
54 | } elseif ($item.Source -eq '') {
55 | "$($item.Name)@$($item.Version)"
56 | } else {
57 | $item.Source
58 | }
59 |
60 | & "$PSScriptRoot\scoop-install.ps1" $app @instArgs
61 |
62 | if ('Held package' -in $info) {
63 | & "$PSScriptRoot\scoop-hold.ps1" $item.Name @holdArgs
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/libexec/scoop-install.ps1:
--------------------------------------------------------------------------------
1 | # Usage: scoop install [options]
2 | # Summary: Install apps
3 | # Help: e.g. The usual way to install an app (uses your local 'buckets'):
4 | # scoop install git
5 | #
6 | # To install a different version of the app
7 | # (note that this will auto-generate the manifest using current version):
8 | # scoop install gh@2.7.0
9 | #
10 | # To install an app from a manifest at a URL:
11 | # scoop install https://raw.githubusercontent.com/ScoopInstaller/Main/master/bucket/runat.json
12 | #
13 | # To install a different version of the app from a URL:
14 | # scoop install https://raw.githubusercontent.com/ScoopInstaller/Main/master/bucket/neovim.json@0.9.0
15 | #
16 | # To install an app from a manifest on your computer
17 | # scoop install \path\to\app.json
18 | #
19 | # To install an app from a manifest on your computer
20 | # scoop install \path\to\app.json@version
21 | #
22 | # Options:
23 | # -g, --global Install the app globally
24 | # -i, --independent Don't install dependencies automatically
25 | # -k, --no-cache Don't use the download cache
26 | # -s, --skip-hash-check Skip hash validation (use with caution!)
27 | # -u, --no-update-scoop Don't update Scoop before installing if it's outdated
28 | # -a, --arch <32bit|64bit|arm64> Use the specified architecture, if the app supports it
29 |
30 | . "$PSScriptRoot\..\lib\getopt.ps1"
31 | . "$PSScriptRoot\..\lib\json.ps1" # 'autoupdate.ps1' 'manifest.ps1' (indirectly)
32 | . "$PSScriptRoot\..\lib\autoupdate.ps1" # 'generate_user_manifest' (indirectly)
33 | . "$PSScriptRoot\..\lib\manifest.ps1" # 'generate_user_manifest' 'Get-Manifest' 'Select-CurrentVersion' (indirectly)
34 | . "$PSScriptRoot\..\lib\system.ps1"
35 | . "$PSScriptRoot\..\lib\install.ps1"
36 | . "$PSScriptRoot\..\lib\decompress.ps1"
37 | . "$PSScriptRoot\..\lib\shortcuts.ps1"
38 | . "$PSScriptRoot\..\lib\psmodules.ps1"
39 | . "$PSScriptRoot\..\lib\versions.ps1"
40 | . "$PSScriptRoot\..\lib\depends.ps1"
41 | if (get_config USE_SQLITE_CACHE) {
42 | . "$PSScriptRoot\..\lib\database.ps1"
43 | }
44 |
45 | $opt, $apps, $err = getopt $args 'giksua:' 'global', 'independent', 'no-cache', 'skip-hash-check', 'no-update-scoop', 'arch='
46 | if ($err) { "scoop install: $err"; exit 1 }
47 |
48 | $global = $opt.g -or $opt.global
49 | $check_hash = !($opt.s -or $opt.'skip-hash-check')
50 | $independent = $opt.i -or $opt.independent
51 | $use_cache = !($opt.k -or $opt.'no-cache')
52 | $architecture = Get-DefaultArchitecture
53 | try {
54 | $architecture = Format-ArchitectureString ($opt.a + $opt.arch)
55 | } catch {
56 | abort "ERROR: $_"
57 | }
58 |
59 | if (!$apps) { error ' missing'; my_usage; exit 1 }
60 |
61 | if ($global -and !(is_admin)) {
62 | abort 'ERROR: you need admin rights to install global apps'
63 | }
64 |
65 | if (is_scoop_outdated) {
66 | if ($opt.u -or $opt.'no-update-scoop') {
67 | warn "Scoop is out of date."
68 | } else {
69 | & "$PSScriptRoot\scoop-update.ps1"
70 | }
71 | }
72 |
73 | ensure_none_failed $apps
74 |
75 | if ($apps.length -eq 1) {
76 | $app, $null, $version = parse_app $apps
77 | if ($app.EndsWith('.json')) {
78 | $app = [System.IO.Path]::GetFileNameWithoutExtension($app)
79 | }
80 | $curVersion = Select-CurrentVersion -AppName $app -Global:$global
81 | if ($null -eq $version -and $curVersion) {
82 | warn "'$app' ($curVersion) is already installed.`nUse 'scoop update $app$(if ($global) { ' --global' })' to install a new version."
83 | exit 0
84 | }
85 | }
86 |
87 | # get any specific versions that we need to handle first
88 | $specific_versions = $apps | Where-Object {
89 | $null, $null, $version = parse_app $_
90 | return $null -ne $version
91 | }
92 |
93 | # compare object does not like nulls
94 | if ($specific_versions.Count -gt 0) {
95 | $difference = Compare-Object -ReferenceObject $apps -DifferenceObject $specific_versions -PassThru
96 | } else {
97 | $difference = $apps
98 | }
99 |
100 | $specific_versions_paths = $specific_versions | ForEach-Object {
101 | $app, $bucket, $version = parse_app $_
102 | if (installed_manifest $app $version) {
103 | warn "'$app' ($version) is already installed.`nUse 'scoop update $app$(if ($global) { ' --global' })' to install a new version."
104 | continue
105 | }
106 |
107 | generate_user_manifest $app $bucket $version
108 | }
109 | $apps = @((@($specific_versions_paths) + $difference) | Where-Object { $_ } | Select-Object -Unique)
110 |
111 | # remember which were explictly requested so that we can
112 | # differentiate after dependencies are added
113 | $explicit_apps = $apps
114 |
115 | if (!$independent) {
116 | $apps = $apps | Get-Dependency -Architecture $architecture | Select-Object -Unique # adds dependencies
117 | }
118 | ensure_none_failed $apps
119 |
120 | $apps, $skip = prune_installed $apps $global
121 |
122 | $skip | Where-Object { $explicit_apps -contains $_ } | ForEach-Object {
123 | $app, $null, $null = parse_app $_
124 | $version = Select-CurrentVersion -AppName $app -Global:$global
125 | warn "'$app' ($version) is already installed. Skipping."
126 | }
127 |
128 | $suggested = @{ };
129 | if ((Test-Aria2Enabled) -and (get_config 'aria2-warning-enabled' $true)) {
130 | warn "Scoop uses 'aria2c' for multi-connection downloads."
131 | warn "Should it cause issues, run 'scoop config aria2-enabled false' to disable it."
132 | warn "To disable this warning, run 'scoop config aria2-warning-enabled false'."
133 | }
134 | $apps | ForEach-Object { install_app $_ $architecture $global $suggested $use_cache $check_hash }
135 |
136 | show_suggestions $suggested
137 |
138 | exit 0
139 |
--------------------------------------------------------------------------------
/libexec/scoop-list.ps1:
--------------------------------------------------------------------------------
1 | # Usage: scoop list [query]
2 | # Summary: List installed apps
3 | # Help: Lists all installed apps, or the apps matching the supplied query.
4 | param($query)
5 |
6 | . "$PSScriptRoot\..\lib\versions.ps1" # 'Select-CurrentVersion'
7 | . "$PSScriptRoot\..\lib\manifest.ps1" # 'parse_json' 'Select-CurrentVersion' (indirectly)
8 |
9 | $def_arch = Get-DefaultArchitecture
10 | if (-not (Get-FormatData ScoopApps)) {
11 | Update-FormatData "$PSScriptRoot\..\supporting\formats\ScoopTypes.Format.ps1xml"
12 | }
13 |
14 | $local = installed_apps $false | ForEach-Object { @{ name = $_ } }
15 | $global = installed_apps $true | ForEach-Object { @{ name = $_; global = $true } }
16 |
17 | $apps = @($local) + @($global)
18 | if (-not $apps) {
19 | Write-Host "There aren't any apps installed."
20 | exit 1
21 | }
22 |
23 | $list = @()
24 | Write-Host "Installed apps$(if($query) { `" matching '$query'`"}):"
25 | $apps | Where-Object { !$query -or ($_.name -match $query) } | ForEach-Object {
26 | $app = $_.name
27 | $global = $_.global
28 | $item = @{}
29 | $ver = Select-CurrentVersion -AppName $app -Global:$global
30 | $item.Name = $app
31 | $item.Version = $ver
32 |
33 | $install_info_path = "$(versiondir $app $ver $global)\install.json"
34 | $updated = (Get-Item (appdir $app $global)).LastWriteTime
35 | $install_info = $null
36 | if (Test-Path $install_info_path) {
37 | $install_info = parse_json $install_info_path
38 | $updated = (Get-Item $install_info_path).LastWriteTime
39 | }
40 |
41 | $item.Source = if ($install_info.bucket) {
42 | $install_info.bucket
43 | } elseif ($install_info.url) {
44 | if ($install_info.url -eq (usermanifest $app)) { '' }
45 | else { $install_info.url }
46 | }
47 | $item.Updated = $updated
48 |
49 | $info = @()
50 | if ($global) { $info += 'Global install' }
51 | if (failed $app $global) { $info += 'Install failed' }
52 | if ($install_info.hold) { $info += 'Held package' }
53 | if ($install_info.architecture -and $def_arch -ne $install_info.architecture) {
54 | $info += $install_info.architecture
55 | }
56 | $item.Info = $info -join ', '
57 |
58 | $list += [PSCustomObject]$item
59 | }
60 |
61 | $list | Add-Member -TypeName 'ScoopApps' -PassThru
62 | exit 0
63 |
--------------------------------------------------------------------------------
/libexec/scoop-prefix.ps1:
--------------------------------------------------------------------------------
1 | # Usage: scoop prefix
2 | # Summary: Returns the path to the specified app
3 | param($app)
4 |
5 | . "$PSScriptRoot\..\lib\versions.ps1" # 'currentdir' (indirectly)
6 |
7 | if (!$app) {
8 | my_usage
9 | exit 1
10 | }
11 |
12 | $app_path = currentdir $app $false
13 | if (!(Test-Path $app_path)) {
14 | $app_path = currentdir $app $true
15 | }
16 |
17 | if (Test-Path $app_path) {
18 | Write-Output $app_path
19 | } else {
20 | abort "Could not find app path for '$app'."
21 | }
22 |
23 | exit 0
24 |
--------------------------------------------------------------------------------
/libexec/scoop-reset.ps1:
--------------------------------------------------------------------------------
1 | # Usage: scoop reset
2 | # Summary: Reset an app to resolve conflicts
3 | # Help: Used to resolve conflicts in favor of a particular app. For example,
4 | # if you've installed 'python' and 'python27', you can use 'scoop reset' to switch between
5 | # using one or the other.
6 | #
7 | # You can use '*' in place of or `-a`/`--all` switch to reset all apps.
8 |
9 | . "$PSScriptRoot\..\lib\getopt.ps1"
10 | . "$PSScriptRoot\..\lib\manifest.ps1" # 'Select-CurrentVersion' (indirectly)
11 | . "$PSScriptRoot\..\lib\system.ps1" # 'env_add_path' (indirectly)
12 | . "$PSScriptRoot\..\lib\install.ps1"
13 | . "$PSScriptRoot\..\lib\versions.ps1" # 'Select-CurrentVersion'
14 | . "$PSScriptRoot\..\lib\shortcuts.ps1"
15 |
16 | $opt, $apps, $err = getopt $args 'a' 'all'
17 | if($err) { "scoop reset: $err"; exit 1 }
18 | $all = $opt.a -or $opt.all
19 |
20 | if(!$apps -and !$all) { error ' missing'; my_usage; exit 1 }
21 |
22 | if($apps -eq '*' -or $all) {
23 | $local = installed_apps $false | ForEach-Object { ,@($_, $false) }
24 | $global = installed_apps $true | ForEach-Object { ,@($_, $true) }
25 | $apps = @($local) + @($global)
26 | }
27 |
28 | $apps | ForEach-Object {
29 | ($app, $global) = $_
30 |
31 | $app, $bucket, $version = parse_app $app
32 |
33 | if(($global -eq $null) -and (installed $app $true)) {
34 | # set global flag when running reset command on specific app
35 | $global = $true
36 | }
37 |
38 | if($app -eq 'scoop') {
39 | # skip scoop
40 | return
41 | }
42 |
43 | if(!(installed $app)) {
44 | error "'$app' isn't installed"
45 | return
46 | }
47 |
48 | if ($null -eq $version) {
49 | $version = Select-CurrentVersion -AppName $app -Global:$global
50 | }
51 |
52 | $manifest = installed_manifest $app $version $global
53 | # if this is null we know the version they're resetting to
54 | # is not installed
55 | if ($manifest -eq $null) {
56 | error "'$app ($version)' isn't installed"
57 | return
58 | }
59 |
60 | if($global -and !(is_admin)) {
61 | warn "'$app' ($version) is a global app. You need admin rights to reset it. Skipping."
62 | return
63 | }
64 |
65 | write-host "Resetting $app ($version)."
66 |
67 | $dir = Convert-Path (versiondir $app $version $global)
68 | $original_dir = $dir
69 | $persist_dir = persistdir $app $global
70 |
71 | #region Workaround for #2952
72 | if (test_running_process $app $global) {
73 | return
74 | }
75 | #endregion Workaround for #2952
76 |
77 | $install = install_info $app $version $global
78 | $architecture = $install.architecture
79 |
80 | $dir = link_current $dir
81 | create_shims $manifest $dir $global $architecture
82 | create_startmenu_shortcuts $manifest $dir $global $architecture
83 | # unset all potential old env before re-adding
84 | env_rm_path $manifest $dir $global $architecture
85 | env_rm $manifest $global $architecture
86 | env_add_path $manifest $dir $global $architecture
87 | env_set $manifest $global $architecture
88 | # unlink all potential old link before re-persisting
89 | unlink_persist_data $manifest $original_dir
90 | persist_data $manifest $original_dir $persist_dir
91 | persist_permission $manifest $global
92 | }
93 |
94 | exit 0
95 |
--------------------------------------------------------------------------------
/libexec/scoop-status.ps1:
--------------------------------------------------------------------------------
1 | # Usage: scoop status
2 | # Summary: Show status and check for new app versions
3 | # Help: Options:
4 | # -l, --local Checks the status for only the locally installed apps,
5 | # and disables remote fetching/checking for Scoop and buckets
6 |
7 | . "$PSScriptRoot\..\lib\manifest.ps1" # 'manifest' 'parse_json' "install_info"
8 | . "$PSScriptRoot\..\lib\versions.ps1" # 'Select-CurrentVersion'
9 |
10 | # check if scoop needs updating
11 | $currentdir = versiondir 'scoop' 'current'
12 | $needs_update = $false
13 | $bucket_needs_update = $false
14 | $script:network_failure = $false
15 | $no_remotes = $args[0] -eq '-l' -or $args[0] -eq '--local'
16 | if (!(Get-Command git -ErrorAction SilentlyContinue)) { $no_remotes = $true }
17 | $list = @()
18 | if (!(Get-FormatData ScoopStatus)) {
19 | Update-FormatData "$PSScriptRoot\..\supporting\formats\ScoopTypes.Format.ps1xml"
20 | }
21 |
22 | function Test-UpdateStatus($repopath) {
23 | if (Test-Path "$repopath\.git") {
24 | Invoke-Git -Path $repopath -ArgumentList @('fetch', '-q', 'origin')
25 | $script:network_failure = 128 -eq $LASTEXITCODE
26 | $branch = Invoke-Git -Path $repopath -ArgumentList @('branch', '--show-current')
27 | $commits = Invoke-Git -Path $repopath -ArgumentList @('log', "HEAD..origin/$branch", '--oneline')
28 | if ($commits) { return $true }
29 | else { return $false }
30 | } else {
31 | return $true
32 | }
33 | }
34 |
35 | if (!$no_remotes) {
36 | $needs_update = Test-UpdateStatus $currentdir
37 | foreach ($bucket in Get-LocalBucket) {
38 | if (Test-UpdateStatus (Find-BucketDirectory $bucket -Root)) {
39 | $bucket_needs_update = $true
40 | break
41 | }
42 | }
43 | }
44 |
45 | if ($needs_update) {
46 | warn "Scoop out of date. Run 'scoop update' to get the latest changes."
47 | } elseif ($bucket_needs_update) {
48 | warn "Scoop bucket(s) out of date. Run 'scoop update' to get the latest changes."
49 | } elseif (!$script:network_failure -and !$no_remotes) {
50 | success 'Scoop is up to date.'
51 | }
52 |
53 | $true, $false | ForEach-Object { # local and global apps
54 | $global = $_
55 | $dir = appsdir $global
56 | if (!(Test-Path $dir)) { return }
57 |
58 | Get-ChildItem $dir | Where-Object name -NE 'scoop' | ForEach-Object {
59 | $app = $_.name
60 | $status = app_status $app $global
61 | if (!$status.outdated -and !$status.failed -and !$status.removed -and !$status.missing_deps) { return }
62 |
63 | $item = [ordered]@{}
64 | $item.Name = $app
65 | $item.'Installed Version' = $status.version
66 | $item.'Latest Version' = if ($status.outdated) { $status.latest_version } else { "" }
67 | $item.'Missing Dependencies' = $status.missing_deps -Split ' ' -Join ' | '
68 | $info = @()
69 | if ($status.failed) { $info += 'Install failed' }
70 | if ($status.hold) { $info += 'Held package' }
71 | if ($status.removed) { $info += 'Manifest removed' }
72 | $item.Info = $info -join ', '
73 | $list += [PSCustomObject]$item
74 | }
75 | }
76 |
77 | if ($list.Length -eq 0 -and !$needs_update -and !$bucket_needs_update -and !$script:network_failure) {
78 | success 'Everything is ok!'
79 | }
80 |
81 | $list | Add-Member -TypeName ScoopStatus -PassThru
82 |
83 | exit 0
84 |
--------------------------------------------------------------------------------
/libexec/scoop-unhold.ps1:
--------------------------------------------------------------------------------
1 | # Usage: scoop unhold
2 | # Summary: Unhold an app to enable updates
3 | # Help: To unhold a user-scoped app:
4 | # scoop unhold
5 | #
6 | # To unhold a global app:
7 | # scoop unhold -g
8 | #
9 | # Options:
10 | # -g, --global Unhold globally installed apps
11 |
12 | . "$PSScriptRoot\..\lib\getopt.ps1"
13 | . "$PSScriptRoot\..\lib\json.ps1" # 'save_install_info' (indirectly)
14 | . "$PSScriptRoot\..\lib\manifest.ps1" # 'install_info' 'Select-CurrentVersion' (indirectly)
15 | . "$PSScriptRoot\..\lib\versions.ps1" # 'Select-CurrentVersion'
16 |
17 | $opt, $apps, $err = getopt $args 'g' 'global'
18 | if ($err) { "scoop unhold: $err"; exit 1 }
19 |
20 | $global = $opt.g -or $opt.global
21 |
22 | if (!$apps) {
23 | my_usage
24 | exit 1
25 | }
26 |
27 | if ($global -and !(is_admin)) {
28 | error 'You need admin rights to unhold a global app.'
29 | exit 1
30 | }
31 |
32 | $apps | ForEach-Object {
33 | $app = $_
34 |
35 | if ($app -eq 'scoop') {
36 | set_config HOLD_UPDATE_UNTIL $null | Out-Null
37 | success "$app is no longer held and can be updated again."
38 | return
39 | }
40 | if (!(installed $app $global)) {
41 | if ($global) {
42 | error "'$app' is not installed globally."
43 | } else {
44 | error "'$app' is not installed."
45 | }
46 | return
47 | }
48 |
49 | if (get_config NO_JUNCTION){
50 | $version = Select-CurrentVersion -App $app -Global:$global
51 | } else {
52 | $version = 'current'
53 | }
54 | $dir = versiondir $app $version $global
55 | $json = install_info $app $version $global
56 | if (!$json) {
57 | error "Failed to unhold '$app'"
58 | continue
59 | }
60 | $install = @{}
61 | $json | Get-Member -MemberType Properties | ForEach-Object { $install.Add($_.Name, $json.($_.Name)) }
62 | if (!$install.hold) {
63 | info "'$app' is not held."
64 | continue
65 | }
66 | $install.hold = $null
67 | save_install_info $install $dir
68 | success "$app is no longer held and can be updated again."
69 | }
70 |
71 | exit $exitcode
72 |
--------------------------------------------------------------------------------
/libexec/scoop-uninstall.ps1:
--------------------------------------------------------------------------------
1 | # Usage: scoop uninstall [options]
2 | # Summary: Uninstall an app
3 | # Help: e.g. scoop uninstall git
4 | #
5 | # Options:
6 | # -g, --global Uninstall a globally installed app
7 | # -p, --purge Remove all persistent data
8 |
9 | . "$PSScriptRoot\..\lib\getopt.ps1"
10 | . "$PSScriptRoot\..\lib\manifest.ps1" # 'Get-Manifest' 'Select-CurrentVersion' (indirectly)
11 | . "$PSScriptRoot\..\lib\system.ps1"
12 | . "$PSScriptRoot\..\lib\install.ps1"
13 | . "$PSScriptRoot\..\lib\shortcuts.ps1"
14 | . "$PSScriptRoot\..\lib\psmodules.ps1"
15 | . "$PSScriptRoot\..\lib\versions.ps1" # 'Select-CurrentVersion'
16 |
17 | # options
18 | $opt, $apps, $err = getopt $args 'gp' 'global', 'purge'
19 |
20 | if ($err) {
21 | error "scoop uninstall: $err"
22 | exit 1
23 | }
24 |
25 | $global = $opt.g -or $opt.global
26 | $purge = $opt.p -or $opt.purge
27 |
28 | if (!$apps) {
29 | error ' missing'
30 | my_usage
31 | exit 1
32 | }
33 |
34 | if ($global -and !(is_admin)) {
35 | error 'You need admin rights to uninstall global apps.'
36 | exit 1
37 | }
38 |
39 | if ($apps -eq 'scoop') {
40 | & "$PSScriptRoot\..\bin\uninstall.ps1" $global $purge
41 | exit
42 | }
43 |
44 | $apps = Confirm-InstallationStatus $apps -Global:$global
45 | if (!$apps) { exit 0 }
46 |
47 | :app_loop foreach ($_ in $apps) {
48 | ($app, $global) = $_
49 |
50 | $version = Select-CurrentVersion -AppName $app -Global:$global
51 | $appDir = appdir $app $global
52 | if ($version) {
53 | Write-Host "Uninstalling '$app' ($version)."
54 |
55 | $dir = versiondir $app $version $global
56 | $persist_dir = persistdir $app $global
57 |
58 | $manifest = installed_manifest $app $version $global
59 | $install = install_info $app $version $global
60 | $architecture = $install.architecture
61 |
62 | Invoke-HookScript -HookType 'pre_uninstall' -Manifest $manifest -Arch $architecture
63 |
64 | #region Workaround for #2952
65 | if (test_running_process $app $global) {
66 | continue
67 | }
68 | #endregion Workaround for #2952
69 |
70 | try {
71 | Test-Path $dir -ErrorAction Stop | Out-Null
72 | } catch [UnauthorizedAccessException] {
73 | error "Access denied: $dir. You might need to restart."
74 | continue
75 | }
76 |
77 | Invoke-Installer -Path $dir -Manifest $manifest -ProcessorArchitecture $architecture -Uninstall
78 | rm_shims $app $manifest $global $architecture
79 | rm_startmenu_shortcuts $manifest $global $architecture
80 |
81 | # If a junction was used during install, that will have been used
82 | # as the reference directory. Otherwise it will just be the version
83 | # directory.
84 | $refdir = unlink_current $dir
85 |
86 | uninstall_psmodule $manifest $refdir $global
87 |
88 | env_rm_path $manifest $refdir $global $architecture
89 | env_rm $manifest $global $architecture
90 |
91 | try {
92 | # unlink all potential old link before doing recursive Remove-Item
93 | unlink_persist_data $manifest $dir
94 | Remove-Item $dir -Recurse -Force -ErrorAction Stop
95 | } catch {
96 | if (Test-Path $dir) {
97 | error "Couldn't remove '$(friendly_path $dir)'; it may be in use."
98 | continue
99 | }
100 | }
101 |
102 | Invoke-HookScript -HookType 'post_uninstall' -Manifest $manifest -Arch $architecture
103 | }
104 | # remove older versions
105 | $oldVersions = @(Get-ChildItem $appDir -Name -Exclude 'current')
106 | foreach ($version in $oldVersions) {
107 | Write-Host "Removing older version ($version)."
108 | $dir = versiondir $app $version $global
109 | try {
110 | # unlink all potential old link before doing recursive Remove-Item
111 | unlink_persist_data $manifest $dir
112 | Remove-Item $dir -Recurse -Force -ErrorAction Stop
113 | } catch {
114 | error "Couldn't remove '$(friendly_path $dir)'; it may be in use."
115 | continue app_loop
116 | }
117 | }
118 | if (Test-Path ($currentDir = Join-Path $appDir 'current')) {
119 | attrib $currentDir -R /L
120 | Remove-Item $currentDir -ErrorAction Stop -Force
121 | }
122 | if (!(Get-ChildItem $appDir)) {
123 | try {
124 | # if last install failed, the directory seems to be locked and this
125 | # will throw an error about the directory not existing
126 | Remove-Item $appdir -Recurse -Force -ErrorAction Stop
127 | } catch {
128 | if ((Test-Path $appdir)) { throw } # only throw if the dir still exists
129 | }
130 | }
131 |
132 | # purge persistant data
133 | if ($purge) {
134 | Write-Host 'Removing persisted data.'
135 | $persist_dir = persistdir $app $global
136 |
137 | if (Test-Path $persist_dir) {
138 | try {
139 | Remove-Item $persist_dir -Recurse -Force -ErrorAction Stop
140 | } catch {
141 | error "Couldn't remove '$(friendly_path $persist_dir)'; it may be in use."
142 | continue
143 | }
144 | }
145 | }
146 |
147 | success "'$app' was uninstalled."
148 | }
149 |
150 | exit 0
151 |
--------------------------------------------------------------------------------
/libexec/scoop-which.ps1:
--------------------------------------------------------------------------------
1 | # Usage: scoop which
2 | # Summary: Locate a shim/executable (similar to 'which' on Linux)
3 | # Help: Locate the path to a shim/executable that was installed with Scoop (similar to 'which' on Linux)
4 | param($command)
5 |
6 | if (!$command) {
7 | error ' missing'
8 | my_usage
9 | exit 1
10 | }
11 |
12 | $path = Get-CommandPath $command
13 |
14 | if ($null -eq $path) {
15 | warn "'$command' not found, not a scoop shim, or a broken shim."
16 | exit 2
17 | } else {
18 | friendly_path $path
19 | exit 0
20 | }
21 |
--------------------------------------------------------------------------------
/supporting/formats/ScoopTypes.Format.ps1xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ScoopAppsType
6 |
7 | ScoopApps
8 |
9 |
10 |
11 |
12 |
13 |
14 | Name
15 |
16 |
17 | Version
18 |
19 |
20 | Source
21 |
22 |
23 | Updated
24 | yyyy-MM-dd HH:mm:ss
25 |
26 |
27 | Info
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | ScoopShimsType
36 |
37 | ScoopShims
38 |
39 |
40 |
41 |
42 |
43 |
44 | Name
45 |
46 |
47 | Source
48 |
49 |
50 | Alternatives
51 |
52 |
53 | IsGlobal
54 |
55 |
56 | IsHidden
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | ScoopStatusType
65 |
66 | ScoopStatus
67 |
68 |
69 |
70 |
71 |
72 |
73 | Name
74 |
75 |
76 | Installed Version
77 |
78 |
79 | Latest Version
80 |
81 |
82 | Missing Dependencies
83 |
84 |
85 | Info
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/supporting/shims/71/checksum.sha256:
--------------------------------------------------------------------------------
1 | 70d4690b8ac3b3f715f537cdea6e07a39fda4bc0347bf6b958e4f3ff2f0e04d4 shim.exe
2 |
--------------------------------------------------------------------------------
/supporting/shims/71/checksum.sha512:
--------------------------------------------------------------------------------
1 | ecde07b32192846c4885cf4d2208eedc170765ea115ae49b81509fed0ce474e21064100bb2f3d815ee79f1c12463d32ef013d4182647eae71855cd18e4196176 shim.exe
2 |
--------------------------------------------------------------------------------
/supporting/shims/71/shim.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ScoopInstaller/Scoop/859d1db51bcc840903d5280567846ae2f7207ca2/supporting/shims/71/shim.exe
--------------------------------------------------------------------------------
/supporting/shims/kiennq/checksum.sha256:
--------------------------------------------------------------------------------
1 | 410f84fe347cf55f92861ea3899d30b2d84a8bbc56bb3451d74697a4a0610b25 *shim.exe
2 |
--------------------------------------------------------------------------------
/supporting/shims/kiennq/checksum.sha512:
--------------------------------------------------------------------------------
1 | 9ce94adf48f7a31ab5773465582728c39db6f11a560fc43316fe6c1ad0a7b69a76aa3f9b52bb6b2e3be8043e4920985c8ca0bf157be9bf1e4a5a4d7c4ed195ba *shim.exe
2 |
--------------------------------------------------------------------------------
/supporting/shims/kiennq/shim.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ScoopInstaller/Scoop/859d1db51bcc840903d5280567846ae2f7207ca2/supporting/shims/kiennq/shim.exe
--------------------------------------------------------------------------------
/supporting/shims/kiennq/version.txt:
--------------------------------------------------------------------------------
1 | v3.1.1
2 |
--------------------------------------------------------------------------------
/supporting/shims/scoopcs/checksum.sha256:
--------------------------------------------------------------------------------
1 | 0116068768fc992fc536738396b33db3dafe6b0cf0e6f54f6d1aa8b0331f3cec *shim.exe
2 |
--------------------------------------------------------------------------------
/supporting/shims/scoopcs/checksum.sha512:
--------------------------------------------------------------------------------
1 | d734c528e9f20581ed3c7aa71a458f7dff7e2780fa0c319ccb9c813cd8dbf656bd7e550b81d2aa3ee8775bff9a4e507bc0b25f075697405adca0f47d37835848 *shim.exe
2 |
--------------------------------------------------------------------------------
/supporting/shims/scoopcs/shim.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ScoopInstaller/Scoop/859d1db51bcc840903d5280567846ae2f7207ca2/supporting/shims/scoopcs/shim.exe
--------------------------------------------------------------------------------
/supporting/shims/scoopcs/version.txt:
--------------------------------------------------------------------------------
1 | 1.1.0
2 |
--------------------------------------------------------------------------------
/supporting/validator/.gitignore:
--------------------------------------------------------------------------------
1 | packages/
2 |
--------------------------------------------------------------------------------
/supporting/validator/Scoop.Validator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Text;
5 | using Newtonsoft.Json;
6 | using Newtonsoft.Json.Linq;
7 | using Newtonsoft.Json.Schema;
8 |
9 | namespace Scoop
10 | {
11 | public class JsonParserException : Exception
12 | {
13 | public string FileName { get; set; }
14 | public JsonParserException(string file, string message) : base(message) { this.FileName = file; }
15 | public JsonParserException(string file, string message, Exception inner) : base(message, inner) { this.FileName = file; }
16 | }
17 |
18 | public class Validator
19 | {
20 | private bool CI { get; set; }
21 | public JSchema Schema { get; private set; }
22 | public FileInfo SchemaFile { get; private set; }
23 | public JObject Manifest { get; private set; }
24 | public FileInfo ManifestFile { get; private set; }
25 | public IList Errors { get; private set; }
26 | public string ErrorsAsString
27 | {
28 | get
29 | {
30 | return String.Join(System.Environment.NewLine, this.Errors);
31 | }
32 | }
33 |
34 | private JSchema ParseSchema(string file)
35 | {
36 | try
37 | {
38 | return JSchema.Parse(File.ReadAllText(file, System.Text.Encoding.UTF8));
39 | }
40 | catch (Newtonsoft.Json.JsonReaderException e)
41 | {
42 | throw new JsonParserException(Path.GetFileName(file), e.Message, e);
43 | }
44 | catch (FileNotFoundException e)
45 | {
46 | throw e;
47 | }
48 | }
49 |
50 | private JObject ParseManifest(string file)
51 | {
52 | try
53 | {
54 | return JObject.Parse(File.ReadAllText(file, System.Text.Encoding.UTF8));
55 | }
56 | catch (Newtonsoft.Json.JsonReaderException e)
57 | {
58 | throw new JsonParserException(Path.GetFileName(file), e.Message, e);
59 | }
60 | catch (FileNotFoundException e)
61 | {
62 | throw e;
63 | }
64 | }
65 |
66 | public Validator(string schemaFile)
67 | {
68 | this.SchemaFile = new FileInfo(schemaFile);
69 | this.Errors = new List();
70 | }
71 |
72 | public Validator(string schemaFile, bool ci)
73 | {
74 | this.SchemaFile = new FileInfo(schemaFile);
75 | this.Errors = new List();
76 | this.CI = ci;
77 | }
78 |
79 | public bool Validate(string file)
80 | {
81 | this.ManifestFile = new FileInfo(file);
82 | return this.Validate();
83 | }
84 |
85 | public bool Validate()
86 | {
87 | if (!this.SchemaFile.Exists)
88 | {
89 | Console.WriteLine("ERROR: Please provide schema.json!");
90 | return false;
91 | }
92 | if (!this.ManifestFile.Exists)
93 | {
94 | Console.WriteLine("ERROR: Please provide manifest.json!");
95 | return false;
96 | }
97 | this.Errors.Clear();
98 | try
99 | {
100 | if (this.Schema == null)
101 | {
102 | this.Schema = this.ParseSchema(this.SchemaFile.FullName);
103 | }
104 | this.Manifest = this.ParseManifest(this.ManifestFile.FullName);
105 | }
106 | catch (FileNotFoundException e)
107 | {
108 | this.Errors.Add(e.Message);
109 | }
110 | catch (JsonParserException e)
111 | {
112 | this.Errors.Add(String.Format("{0}{1}: {2}", (this.CI ? " [*] " : ""), e.FileName, e.Message));
113 | }
114 |
115 | if (this.Schema == null || this.Manifest == null)
116 | return false;
117 |
118 | IList validationErrors = new List();
119 |
120 | this.Manifest.IsValid(this.Schema, out validationErrors);
121 |
122 | if (validationErrors.Count == 0)
123 | {
124 | return true;
125 | }
126 | traverseErrors(validationErrors, this.CI ? 3 : 1);
127 |
128 | return (this.Errors.Count == 0);
129 | }
130 |
131 | public void traverseErrors(IList errors, int level = 1) {
132 | if(errors == null) {
133 | return;
134 | }
135 | foreach (ValidationError error in errors)
136 | {
137 | StringBuilder sb = new StringBuilder();
138 | sb.Insert(sb.Length, " ", level * 2);
139 | sb.Insert(sb.Length, this.CI ? "[*] " : "- ");
140 | sb.AppendFormat("Error: {0}\n", error.Message);
141 |
142 | sb.Insert(sb.Length, " ", level * 2);
143 | sb.Insert(sb.Length, this.CI ? " [^] " : " ");
144 | sb.AppendFormat("Line: {0}:{1}:{2}\n", this.ManifestFile.FullName, error.LineNumber, error.LinePosition);
145 |
146 | sb.Insert(sb.Length, " ", level * 2);
147 | sb.Insert(sb.Length, this.CI ? " [^] " : " ");
148 | sb.AppendFormat("Path: {0}/{1}", error.SchemaId, error.ErrorType);
149 |
150 | if(!this.CI) {
151 | sb.Insert(sb.Length, "\n");
152 | }
153 |
154 | this.Errors.Add(sb.ToString());
155 |
156 | if(error.ChildErrors != null || error.ChildErrors.Count > 0) {
157 | traverseErrors(error.ChildErrors, level + 1);
158 | }
159 | }
160 | }
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/supporting/validator/bin/Newtonsoft.Json.Schema.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ScoopInstaller/Scoop/859d1db51bcc840903d5280567846ae2f7207ca2/supporting/validator/bin/Newtonsoft.Json.Schema.dll
--------------------------------------------------------------------------------
/supporting/validator/bin/Newtonsoft.Json.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ScoopInstaller/Scoop/859d1db51bcc840903d5280567846ae2f7207ca2/supporting/validator/bin/Newtonsoft.Json.dll
--------------------------------------------------------------------------------
/supporting/validator/bin/Scoop.Validator.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ScoopInstaller/Scoop/859d1db51bcc840903d5280567846ae2f7207ca2/supporting/validator/bin/Scoop.Validator.dll
--------------------------------------------------------------------------------
/supporting/validator/bin/checksum.sha256:
--------------------------------------------------------------------------------
1 | e1e27af7b07eeedf5ce71a9255f0422816a6fc5849a483c6714e1b472044fa9d *Newtonsoft.Json.dll
2 | 7496d5349a123a6e3696085662b2ff17b156ccdb0e30e0c396ac72d2da36ce1c *Newtonsoft.Json.Schema.dll
3 | 83b1006443e8c340ca4c631614fc2ce0d5cb9a28c851e3b59724299f58b1397f *Scoop.Validator.dll
4 | 87f8f8db2202a3fbef6f431d0b7e20cec9d32095c441927402041f3c4076c1b6 *validator.exe
5 |
--------------------------------------------------------------------------------
/supporting/validator/bin/checksum.sha512:
--------------------------------------------------------------------------------
1 | 56eb7f070929b239642dab729537dde2c2287bdb852ad9e80b5358c74b14bc2b2dded910d0e3b6304ea27eb587e5f19db0a92e1cbae6a70fb20b4ef05057e4ac *Newtonsoft.Json.dll
2 | 78b12beb1e67ac4f6efa0fcba57b4b34ea6a31d8b369934d6b6a6617386ef9939ea453ac262916e5857ce0359eb809424ea33c676a87a8fdfd77a59b2ce96db0 *Newtonsoft.Json.Schema.dll
3 | e9da4370aee4df47eedcf15d9749712eee513e5a9115b808617ddfcfde5bc47a0410edfb57508fcf51033c0be967611b2fd2c2ba944de7290c020cc67f77ac57 *Scoop.Validator.dll
4 | 58a0c37e98cac17822c7756bf6686a5fb74e711b8d986d13bd2f689f6b3b1f485fcd908d92cbc6a162a0e5974c2c5a43de57d15f1996be0aa405e41ec2ec8393 *validator.exe
5 |
--------------------------------------------------------------------------------
/supporting/validator/bin/validator.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ScoopInstaller/Scoop/859d1db51bcc840903d5280567846ae2f7207ca2/supporting/validator/bin/validator.exe
--------------------------------------------------------------------------------
/supporting/validator/build.ps1:
--------------------------------------------------------------------------------
1 | Param([Switch]$Fast)
2 | Push-Location $PSScriptRoot
3 | . "$PSScriptRoot\..\..\lib\core.ps1"
4 | . "$PSScriptRoot\..\..\lib\install.ps1"
5 |
6 | if (!$Fast) {
7 | Write-Host 'Install dependencies ...'
8 | & "$PSScriptRoot\install.ps1"
9 | }
10 |
11 | $output = "$PSScriptRoot\bin"
12 | if (!$Fast) {
13 | Get-ChildItem "$PSScriptRoot\packages\Newtonsoft.*\lib\net45\*.dll" -File | ForEach-Object { Copy-Item $_ $output }
14 | }
15 | Write-Output 'Compiling Scoop.Validator.cs ...'
16 | & "$PSScriptRoot\packages\Microsoft.Net.Compilers.Toolset\tasks\net472\csc.exe" -deterministic -platform:anycpu -nologo -optimize -target:library -reference:"$output\Newtonsoft.Json.dll" -reference:"$output\Newtonsoft.Json.Schema.dll" -out:"$output\Scoop.Validator.dll" Scoop.Validator.cs
17 | Write-Output 'Compiling validator.cs ...'
18 | & "$PSScriptRoot\packages\Microsoft.Net.Compilers.Toolset\tasks\net472\csc.exe" -deterministic -platform:anycpu -nologo -optimize -target:exe -reference:"$output\Scoop.Validator.dll" -reference:"$output\Newtonsoft.Json.dll" -reference:"$output\Newtonsoft.Json.Schema.dll" -out:"$output\validator.exe" validator.cs
19 |
20 | Write-Output 'Computing checksums ...'
21 | Remove-Item "$PSScriptRoot\bin\checksum.sha256" -ErrorAction Ignore
22 | Remove-Item "$PSScriptRoot\bin\checksum.sha512" -ErrorAction Ignore
23 | Get-ChildItem "$PSScriptRoot\bin\*" -Include *.exe, *.dll | ForEach-Object {
24 | "$((Get-FileHash -Path $_ -Algorithm SHA256).Hash.ToLower()) *$($_.Name)" | Out-File "$PSScriptRoot\bin\checksum.sha256" -Append -Encoding oem
25 | "$((Get-FileHash -Path $_ -Algorithm SHA512).Hash.ToLower()) *$($_.Name)" | Out-File "$PSScriptRoot\bin\checksum.sha512" -Append -Encoding oem
26 | }
27 | Pop-Location
28 |
--------------------------------------------------------------------------------
/supporting/validator/install.ps1:
--------------------------------------------------------------------------------
1 | # https://github.com/edymtt/nugetstandalone
2 | $destinationFolder = "$PSScriptRoot\packages"
3 | if ((Test-Path -Path $destinationFolder)) {
4 | Remove-Item -Path $destinationFolder -Recurse | Out-Null
5 | }
6 |
7 | New-Item $destinationFolder -Type Directory | Out-Null
8 | nuget install packages.config -o $destinationFolder -ExcludeVersion
9 |
--------------------------------------------------------------------------------
/supporting/validator/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 |
--------------------------------------------------------------------------------
/supporting/validator/update.ps1:
--------------------------------------------------------------------------------
1 | # https://github.com/edymtt/nugetstandalone
2 | $destinationFolder = "$PSScriptRoot\packages"
3 | if (!(Test-Path -Path $destinationFolder)) {
4 | Write-Host -f Red "Run .\install.ps1 first!"
5 | exit 1
6 | }
7 |
8 | nuget update packages.config -r $destinationFolder
9 | Remove-Item $destinationFolder -Force -Recurse | Out-Null
10 | nuget install packages.config -o $destinationFolder -ExcludeVersion
11 |
--------------------------------------------------------------------------------
/supporting/validator/validator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Linq;
6 |
7 | namespace Scoop
8 | {
9 | public class Program
10 | {
11 | public static int Main(string[] args)
12 | {
13 | bool ci = String.Format("{0}", Environment.GetEnvironmentVariable("CI")).ToLower() == "true";
14 | bool valid = true;
15 |
16 | if (args.Length < 2)
17 | {
18 | Console.WriteLine("Usage: validator.exe [...]");
19 | return 1;
20 | }
21 |
22 |
23 | IList manifests = args.ToList();
24 | String schema = manifests.First();
25 | manifests.RemoveAt(0);
26 | String combinedArgs = String.Join("", manifests);
27 | if(combinedArgs.Contains("*") || combinedArgs.Contains("?")) {
28 | try {
29 | var path = new Uri(Path.Combine(Directory.GetCurrentDirectory(), combinedArgs)).LocalPath;
30 | var drive = Path.GetPathRoot(path);
31 | var pattern = path.Replace(drive, "");
32 | manifests = Directory.GetFiles(drive, pattern).ToList();
33 | } catch (System.ArgumentException ex) {
34 | Console.WriteLine("Invalid path provided! ({0})", ex.Message);
35 | return 1;
36 | }
37 | }
38 |
39 | Scoop.Validator validator = new Scoop.Validator(schema, ci);
40 | foreach(var manifest in manifests) {
41 | if (validator.Validate(manifest))
42 | {
43 | if(ci) {
44 | Console.WriteLine(" [+] {0} validates against the schema!", Path.GetFileName(manifest));
45 | } else {
46 | Console.WriteLine("- {0} validates against the schema!", Path.GetFileName(manifest));
47 | }
48 | }
49 | else
50 | {
51 | if(ci) {
52 | Console.WriteLine(" [-] {0} has {1} Error{2}!", Path.GetFileName(manifest), validator.Errors.Count, validator.Errors.Count > 1 ? "s" : "");
53 | } else {
54 | Console.WriteLine("- {0} has {1} Error{2}!", Path.GetFileName(manifest), validator.Errors.Count, validator.Errors.Count > 1 ? "s" : "");
55 | }
56 | valid = false;
57 | foreach (var error in validator.Errors)
58 | {
59 | Console.WriteLine(error);
60 | }
61 | }
62 | }
63 |
64 | return valid ? 0 : 1;
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/supporting/validator/validator.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
8 |
9 | Debug
10 | AnyCPU
11 | {8EB9B38A-1BAB-4D89-B4CB-ACE5E7C1073B}
12 | Exe
13 | Scoop.Validator
14 | Scoop.Validator
15 | v4.5.0
16 | 512
17 | true
18 |
19 |
20 |
22 | packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll
23 | True
24 |
25 |
27 | packages\Newtonsoft.Json.Schema.4.0.1\lib\net45\Newtonsoft.Json.Schema.dll
28 | True
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | This project references NuGet package(s) that are missing on this computer.
50 | Enable NuGet Package Restore to download them. For more information, see
51 | http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}.
52 |
53 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/test/Import-Bucket-Tests.ps1:
--------------------------------------------------------------------------------
1 | #Requires -Version 5.1
2 | #Requires -Modules @{ ModuleName = 'BuildHelpers'; ModuleVersion = '2.0.1' }
3 | #Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.2.0' }
4 | param(
5 | [String] $BucketPath = $MyInvocation.PSScriptRoot
6 | )
7 |
8 | . "$PSScriptRoot\Scoop-00File.Tests.ps1" -TestPath $BucketPath
9 |
10 | Describe 'Manifest validates against the schema' {
11 | BeforeDiscovery {
12 | $bucketDir = if (Test-Path "$BucketPath\bucket") {
13 | "$BucketPath\bucket"
14 | } else {
15 | $BucketPath
16 | }
17 | if ($env:CI -eq $true) {
18 | Set-BuildEnvironment -Force
19 | $manifestFiles = @(Get-GitChangedFile -Path $bucketDir -Include '*.json' -Commit $env:BHCommitHash)
20 | } else {
21 | $manifestFiles = (Get-ChildItem $bucketDir -Filter '*.json' -Recurse).FullName
22 | }
23 | }
24 | BeforeAll {
25 | Add-Type -Path "$PSScriptRoot\..\supporting\validator\bin\Scoop.Validator.dll"
26 | # Could not use backslash '\' in Linux/macOS for .NET object 'Scoop.Validator'
27 | $validator = New-Object Scoop.Validator("$PSScriptRoot/../schema.json", $true)
28 | $global:quotaExceeded = $false
29 | }
30 | It '<_>' -TestCases $manifestFiles {
31 | if ($global:quotaExceeded) {
32 | Set-ItResult -Skipped -Because 'Schema validation limit exceeded.'
33 | } else {
34 | $file = $_ # exception handling may overwrite $_
35 | try {
36 | $validator.Validate($file)
37 | if ($validator.Errors.Count -gt 0) {
38 | Write-Host " [-] $_ has $($validator.Errors.Count) Error$(If($validator.Errors.Count -gt 1) { 's' })!" -ForegroundColor Red
39 | Write-Host $validator.ErrorsAsString -ForegroundColor Yellow
40 | }
41 | $validator.Errors.Count | Should -Be 0
42 | } catch {
43 | if ($_.Exception.Message -like '*The free-quota limit of 1000 schema validations per hour has been reached.*') {
44 | $global:quotaExceeded = $true
45 | Set-ItResult -Skipped -Because 'Schema validation limit exceeded.'
46 | } else {
47 | throw
48 | }
49 | }
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/test/Scoop-00File.Tests.ps1:
--------------------------------------------------------------------------------
1 | param(
2 | [String] $TestPath = "$PSScriptRoot\.."
3 | )
4 |
5 | BeforeDiscovery {
6 | $project_file_exclusions = @(
7 | '[\\/]\.git[\\/]',
8 | '\.sublime-workspace$',
9 | '\.DS_Store$',
10 | 'supporting(\\|/)validator(\\|/)packages(\\|/)*'
11 | )
12 | $repo_files = (Get-ChildItem $TestPath -File -Recurse).FullName |
13 | Where-Object { $_ -inotmatch $($project_file_exclusions -join '|') }
14 | }
15 |
16 | Describe 'Code Syntax' -ForEach @(, $repo_files) -Tag 'File' {
17 | BeforeAll {
18 | $files = @(
19 | $_ | Where-Object { $_ -imatch '.(ps1|psm1)$' }
20 | )
21 | function Test-PowerShellSyntax {
22 | # ref: http://powershell.org/wp/forums/topic/how-to-check-syntax-of-scripts-automatically @@ https://archive.is/xtSv6
23 | # originally created by Alexander Petrovskiy & Dave Wyatt
24 | [CmdletBinding()]
25 | param (
26 | [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
27 | [string[]]
28 | $Path
29 | )
30 |
31 | process {
32 | foreach ($scriptPath in $Path) {
33 | $contents = Get-Content -Path $scriptPath
34 |
35 | if ($null -eq $contents) {
36 | continue
37 | }
38 |
39 | $errors = $null
40 | $null = [System.Management.Automation.PSParser]::Tokenize($contents, [ref]$errors)
41 |
42 | New-Object psobject -Property @{
43 | Path = $scriptPath
44 | SyntaxErrorsFound = ($errors.Count -gt 0)
45 | }
46 | }
47 | }
48 | }
49 |
50 | }
51 |
52 | It 'PowerShell code files do not contain syntax errors' {
53 | $badFiles = @(
54 | foreach ($file in $files) {
55 | if ( (Test-PowerShellSyntax $file).SyntaxErrorsFound ) {
56 | $file
57 | }
58 | }
59 | )
60 |
61 | if ($badFiles.Count -gt 0) {
62 | throw "The following files have syntax errors: `r`n`r`n$($badFiles -join "`r`n")"
63 | }
64 | }
65 |
66 | }
67 |
68 | Describe 'Style constraints for non-binary project files' -ForEach @(, $repo_files) -Tag 'File' {
69 | BeforeAll {
70 | $files = @(
71 | # gather all files except '*.exe', '*.zip', or any .git repository files
72 | $_ |
73 | Where-Object { $_ -inotmatch '(.exe|.zip|.dll)$' } |
74 | Where-Object { $_ -inotmatch '(unformatted)' }
75 | )
76 | }
77 |
78 | It 'files do not contain leading UTF-8 BOM' {
79 | # UTF-8 BOM == 0xEF 0xBB 0xBF
80 | # see http://www.powershellmagazine.com/2012/12/17/pscxtip-how-to-determine-the-byte-order-mark-of-a-text-file @@ https://archive.is/RgT42
81 | # ref: http://poshcode.org/2153 @@ https://archive.is/sGnnu
82 | $badFiles = @(
83 | foreach ($file in $files) {
84 | if ((Get-Command Get-Content).parameters.ContainsKey('AsByteStream')) {
85 | # PowerShell Core (6.0+) '-Encoding byte' is replaced by '-AsByteStream'
86 | $content = ([char[]](Get-Content $file -AsByteStream -TotalCount 3) -join '')
87 | } else {
88 | $content = ([char[]](Get-Content $file -Encoding byte -TotalCount 3) -join '')
89 | }
90 | if ([regex]::match($content, '(?ms)^\xEF\xBB\xBF').success) {
91 | $file
92 | }
93 | }
94 | )
95 |
96 | if ($badFiles.Count -gt 0) {
97 | throw "The following files have utf-8 BOM: `r`n`r`n$($badFiles -join "`r`n")"
98 | }
99 | }
100 |
101 | It 'files end with a newline' {
102 | $badFiles = @(
103 | foreach ($file in $files) {
104 | # Ignore previous TestResults.xml
105 | if ($file -match 'TestResults.xml') {
106 | continue
107 | }
108 | $string = [System.IO.File]::ReadAllText($file)
109 | if ($string.Length -gt 0 -and $string[-1] -ne "`n") {
110 | $file
111 | }
112 | }
113 | )
114 |
115 | if ($badFiles.Count -gt 0) {
116 | throw "The following files do not end with a newline: `r`n`r`n$($badFiles -join "`r`n")"
117 | }
118 | }
119 |
120 | It 'file newlines are CRLF' {
121 | $badFiles = @(
122 | foreach ($file in $files) {
123 | $content = [System.IO.File]::ReadAllText($file)
124 | if (!$content) {
125 | throw "File contents are null: $($file)"
126 | }
127 | $lines = [regex]::split($content, '\r\n')
128 | $lineCount = $lines.Count
129 |
130 | for ($i = 0; $i -lt $lineCount; $i++) {
131 | if ( [regex]::match($lines[$i], '\r|\n').success ) {
132 | $file
133 | break
134 | }
135 | }
136 | }
137 | )
138 |
139 | if ($badFiles.Count -gt 0) {
140 | throw "The following files have non-CRLF line endings: `r`n`r`n$($badFiles -join "`r`n")"
141 | }
142 | }
143 |
144 | It 'files have no lines containing trailing whitespace' {
145 | $badLines = @(
146 | foreach ($file in $files) {
147 | # Ignore previous TestResults.xml
148 | if ($file -match 'TestResults.xml') {
149 | continue
150 | }
151 | $lines = [System.IO.File]::ReadAllLines($file)
152 | $lineCount = $lines.Count
153 |
154 | for ($i = 0; $i -lt $lineCount; $i++) {
155 | if ($lines[$i] -match '\s+$') {
156 | 'File: {0}, Line: {1}' -f $file, ($i + 1)
157 | }
158 | }
159 | }
160 | )
161 |
162 | if ($badLines.Count -gt 0) {
163 | throw "The following $($badLines.Count) lines contain trailing whitespace: `r`n`r`n$($badLines -join "`r`n")"
164 | }
165 | }
166 |
167 | It 'any leading whitespace consists only of spaces (excepting makefiles)' {
168 | $badLines = @(
169 | foreach ($file in $files) {
170 | if ($file -inotmatch '(^|.)makefile$') {
171 | $lines = [System.IO.File]::ReadAllLines($file)
172 | $lineCount = $lines.Count
173 |
174 | for ($i = 0; $i -lt $lineCount; $i++) {
175 | if ($lines[$i] -notmatch '^[ ]*(\S|$)') {
176 | 'File: {0}, Line: {1}' -f $file, ($i + 1)
177 | }
178 | }
179 | }
180 | }
181 | )
182 |
183 | if ($badLines.Count -gt 0) {
184 | throw "The following $($badLines.Count) lines contain TABs within leading whitespace: `r`n`r`n$($badLines -join "`r`n")"
185 | }
186 | }
187 |
188 | }
189 |
--------------------------------------------------------------------------------
/test/Scoop-00Linting.Tests.ps1:
--------------------------------------------------------------------------------
1 | Describe 'PSScriptAnalyzer' -Tag 'Linter' {
2 | BeforeDiscovery {
3 | $scriptDir = @('.', 'bin', 'lib', 'libexec', 'test')
4 | }
5 |
6 | BeforeAll {
7 | $lintSettings = "$PSScriptRoot\..\PSScriptAnalyzerSettings.psd1"
8 | }
9 |
10 | It 'PSScriptAnalyzerSettings.ps1 should exist' {
11 | $lintSettings | Should -Exist
12 | }
13 |
14 | Context 'Linting all *.psd1, *.psm1 and *.ps1 files' {
15 | BeforeEach {
16 | $analysis = Invoke-ScriptAnalyzer -Path "$PSScriptRoot\..\$_" -Settings $lintSettings
17 | }
18 | It 'Should pass: <_>' -TestCases $scriptDir {
19 | $analysis | Should -HaveCount 0
20 | if ($analysis) {
21 | foreach ($result in $analysis) {
22 | switch -wildCard ($result.ScriptName) {
23 | '*.psm1' { $type = 'Module' }
24 | '*.ps1' { $type = 'Script' }
25 | '*.psd1' { $type = 'Manifest' }
26 | }
27 | Write-Warning " [*] $($result.Severity): $($result.Message)"
28 | Write-Warning " $($result.RuleName) in $type`: $directory\$($result.ScriptName):$($result.Line)"
29 | }
30 | }
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/test/Scoop-Commands.Tests.ps1:
--------------------------------------------------------------------------------
1 | BeforeAll {
2 | . "$PSScriptRoot\Scoop-TestLib.ps1"
3 | . "$PSScriptRoot\..\lib\core.ps1"
4 | . "$PSScriptRoot\..\lib\commands.ps1"
5 | }
6 |
7 | Describe 'Manipulate Alias' -Tag 'Scoop' {
8 | BeforeAll {
9 | Mock shimdir { "$TestDrive\shims" }
10 | Mock set_config {}
11 | Mock get_config { @{} }
12 |
13 | $shimdir = shimdir
14 | ensure $shimdir
15 | }
16 |
17 | It 'Creates a new alias if it does not exist' {
18 | $alias_script = "$shimdir\scoop-rm.ps1"
19 | $alias_script | Should -Not -Exist
20 |
21 | add_alias 'rm' '"hello, world!"'
22 | & $alias_script | Should -Be 'hello, world!'
23 | }
24 |
25 | It 'Skips an existing alias' {
26 | $alias_script = "$shimdir\scoop-rm.ps1"
27 | Mock abort {}
28 | New-Item $alias_script -Type File -Force
29 | $alias_script | Should -Exist
30 |
31 | add_alias 'rm' '"test"'
32 | Should -Invoke -CommandName abort -Times 1 -ParameterFilter { $msg -eq "File 'scoop-rm.ps1' already exists in shims directory." }
33 | }
34 |
35 | It 'Removes an existing alias' {
36 | $alias_script = "$shimdir\scoop-rm.ps1"
37 | $alias_script | Should -Exist
38 | Mock get_config { @(@{'rm' = 'scoop-rm' }) }
39 | Mock info {}
40 |
41 | rm_alias 'rm'
42 | $alias_script | Should -Not -Exist
43 | Should -Invoke -CommandName info -Times 1 -ParameterFilter { $msg -eq "Removing alias 'rm'..." }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/test/Scoop-Config.Tests.ps1:
--------------------------------------------------------------------------------
1 | BeforeAll {
2 | . "$PSScriptRoot\Scoop-TestLib.ps1"
3 | . "$PSScriptRoot\..\lib\core.ps1"
4 | }
5 |
6 | Describe 'config' -Tag 'Scoop' {
7 | BeforeAll {
8 | $configFile = [IO.Path]::GetTempFileName()
9 | $unicode = [Regex]::Unescape('\u4f60\u597d\u3053\u3093\u306b\u3061\u306f') # 你好こんにちは
10 | }
11 |
12 | AfterAll {
13 | Remove-Item -Path $configFile -Force
14 | }
15 |
16 | It 'load_cfg should return null if config file does not exist' {
17 | load_cfg $configFile | Should -Be $null
18 | }
19 |
20 | It 'set_config should be able to save typed values correctly' {
21 | # number
22 | $scoopConfig = set_config 'one' 1
23 | $scoopConfig.one | Should -BeExactly 1
24 |
25 | # boolean
26 | $scoopConfig = set_config 'two' $true
27 | $scoopConfig.two | Should -BeTrue
28 | $scoopConfig = set_config 'three' $false
29 | $scoopConfig.three | Should -BeFalse
30 |
31 | # underline key
32 | $scoopConfig = set_config 'under_line' 'four'
33 | $scoopConfig.under_line | Should -BeExactly 'four'
34 |
35 | # string
36 | $scoopConfig = set_config 'five' 'not null'
37 |
38 | # datetime
39 | $scoopConfig = set_config 'time' ([System.DateTime]::Parse('2019-03-18T15:22:09.3930000+00:00', $null, [System.Globalization.DateTimeStyles]::AdjustToUniversal))
40 | $scoopConfig.time | Should -BeOfType [System.DateTime]
41 |
42 | # non-ASCII
43 | $scoopConfig = set_config 'unicode' $unicode
44 | $scoopConfig.unicode | Should -Be $unicode
45 | }
46 |
47 | It 'load_cfg should return PSObject if config file exist' {
48 | $scoopConfig = load_cfg $configFile
49 | $scoopConfig | Should -Not -BeNullOrEmpty
50 | $scoopConfig | Should -BeOfType [System.Management.Automation.PSObject]
51 | $scoopConfig.one | Should -BeExactly 1
52 | $scoopConfig.two | Should -BeTrue
53 | $scoopConfig.three | Should -BeFalse
54 | $scoopConfig.under_line | Should -BeExactly 'four'
55 | $scoopConfig.five | Should -Be 'not null'
56 | $scoopConfig.time | Should -BeOfType [System.DateTime]
57 | $scoopConfig.time | Should -Be ([System.DateTime]::Parse('2019-03-18T15:22:09.3930000+00:00', $null, [System.Globalization.DateTimeStyles]::AdjustToUniversal))
58 | $scoopConfig.unicode | Should -Be $unicode
59 | }
60 |
61 | It 'get_config should return exactly the same values' {
62 | $scoopConfig = load_cfg $configFile
63 | (get_config 'one') | Should -BeExactly 1
64 | (get_config 'two') | Should -BeTrue
65 | (get_config 'three') | Should -BeFalse
66 | (get_config 'under_line') | Should -BeExactly 'four'
67 | (get_config 'five') | Should -Be 'not null'
68 | (get_config 'time') | Should -BeOfType [System.DateTime]
69 | (get_config 'time') | Should -Be ([System.DateTime]::Parse('2019-03-18T15:22:09.3930000+00:00', $null, [System.Globalization.DateTimeStyles]::AdjustToUniversal))
70 | (get_config 'unicode') | Should -Be $unicode
71 | }
72 |
73 | It 'set_config should remove a value if being set to $null' {
74 | $scoopConfig = load_cfg $configFile
75 | $scoopConfig = set_config 'five' $null
76 | $scoopConfig.five | Should -BeNullOrEmpty
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/test/Scoop-Depends.Tests.ps1:
--------------------------------------------------------------------------------
1 | BeforeAll {
2 | . "$PSScriptRoot\Scoop-TestLib.ps1"
3 | . "$PSScriptRoot\..\lib\core.ps1"
4 | . "$PSScriptRoot\..\lib\depends.ps1"
5 | . "$PSScriptRoot\..\lib\buckets.ps1"
6 | . "$PSScriptRoot\..\lib\install.ps1"
7 | . "$PSScriptRoot\..\lib\manifest.ps1"
8 | }
9 |
10 | Describe 'Package Dependencies' -Tag 'Scoop' {
11 | Context 'Requirement function' {
12 | It 'Test 7zip requirement' {
13 | Test-7zipRequirement -Uri 'test.xz' | Should -BeTrue
14 | Test-7zipRequirement -Uri 'test.bin' | Should -BeFalse
15 | Test-7zipRequirement -Uri @('test.xz', 'test.bin') | Should -BeTrue
16 | }
17 | It 'Test lessmsi requirement' {
18 | Mock get_config { $true }
19 | Test-LessmsiRequirement -Uri 'test.msi' | Should -BeTrue
20 | Test-LessmsiRequirement -Uri 'test.bin' | Should -BeFalse
21 | Test-LessmsiRequirement -Uri @('test.msi', 'test.bin') | Should -BeTrue
22 | }
23 | It 'Allow $Uri be $null' {
24 | Test-7zipRequirement -Uri $null | Should -BeFalse
25 | Test-LessmsiRequirement -Uri $null | Should -BeFalse
26 | }
27 | }
28 |
29 | Context 'InstallationHelper function' {
30 | BeforeAll {
31 | $working_dir = setup_working 'format/formatted'
32 | $manifest1 = parse_json (Join-Path $working_dir '3-array-with-single-and-multi.json')
33 | $manifest2 = parse_json (Join-Path $working_dir '4-script-block.json')
34 | Mock Test-HelperInstalled { $false }
35 | }
36 | It 'Get helpers from URL' {
37 | Mock get_config { $true }
38 | Get-InstallationHelper -Manifest $manifest1 -Architecture '32bit' | Should -Be @('lessmsi')
39 | }
40 | It 'Get helpers from script' {
41 | Mock get_config { $false }
42 | Get-InstallationHelper -Manifest $manifest2 -Architecture '32bit' | Should -Be @('7zip')
43 | }
44 | It 'Helpers reflect config changes' {
45 | Mock get_config { $false } -ParameterFilter { $name -eq 'USE_LESSMSI' }
46 | Mock get_config { $true } -ParameterFilter { $name -eq 'USE_EXTERNAL_7ZIP' }
47 | Get-InstallationHelper -Manifest $manifest1 -Architecture '32bit' | Should -BeNullOrEmpty
48 | Get-InstallationHelper -Manifest $manifest2 -Architecture '32bit' | Should -BeNullOrEmpty
49 | }
50 | It 'Not return installed helpers' {
51 | Mock get_config { $true } -ParameterFilter { $name -eq 'USE_LESSMSI' }
52 | Mock get_config { $false } -ParameterFilter { $name -eq 'USE_EXTERNAL_7ZIP' }
53 | Mock Test-HelperInstalled { $true }-ParameterFilter { $Helper -eq '7zip' }
54 | Mock Test-HelperInstalled { $false }-ParameterFilter { $Helper -eq 'Lessmsi' }
55 | Get-InstallationHelper -Manifest $manifest1 -Architecture '32bit' | Should -Be @('lessmsi')
56 | Get-InstallationHelper -Manifest $manifest2 -Architecture '32bit' | Should -BeNullOrEmpty
57 | Mock Test-HelperInstalled { $false }-ParameterFilter { $Helper -eq '7zip' }
58 | Mock Test-HelperInstalled { $true }-ParameterFilter { $Helper -eq 'Lessmsi' }
59 | Get-InstallationHelper -Manifest $manifest1 -Architecture '32bit' | Should -BeNullOrEmpty
60 | Get-InstallationHelper -Manifest $manifest2 -Architecture '32bit' | Should -Be @('7zip')
61 | }
62 | }
63 |
64 | Context 'Dependencies resolution' {
65 | BeforeAll {
66 | Mock Test-HelperInstalled { $false }
67 | Mock get_config { $true } -ParameterFilter { $name -eq 'USE_LESSMSI' }
68 | Mock Get-Manifest { 'lessmsi', @{}, $null, $null } -ParameterFilter { $app -eq 'lessmsi' }
69 | Mock Get-Manifest { '7zip', @{ url = 'test.msi' }, $null, $null } -ParameterFilter { $app -eq '7zip' }
70 | Mock Get-Manifest { 'innounp', @{}, $null, $null } -ParameterFilter { $app -eq 'innounp' }
71 | }
72 |
73 | It 'Resolve install dependencies' {
74 | Mock Get-Manifest { 'test', @{ url = 'test.7z' }, $null, $null }
75 | Get-Dependency -AppName 'test' -Architecture '32bit' | Should -Be @('lessmsi', '7zip', 'test')
76 | Mock Get-Manifest { 'test', @{ innosetup = $true }, $null, $null }
77 | Get-Dependency -AppName 'test' -Architecture '32bit' | Should -Be @('innounp', 'test')
78 | }
79 | It 'Resolve script dependencies' {
80 | Mock Get-Manifest { 'test', @{ pre_install = 'Expand-7zipArchive ' }, $null, $null }
81 | Get-Dependency -AppName 'test' -Architecture '32bit' | Should -Be @('lessmsi', '7zip', 'test')
82 | }
83 | It 'Resolve runtime dependencies' {
84 | Mock Get-Manifest { 'depends', @{}, $null, $null } -ParameterFilter { $app -eq 'depends' }
85 | Mock Get-Manifest { 'test', @{ depends = 'depends' }, $null, $null }
86 | Get-Dependency -AppName 'test' -Architecture '32bit' | Should -Be @('depends', 'test')
87 | }
88 | It 'Keep bucket name of app' {
89 | Mock Get-Manifest { 'depends', @{}, 'anotherbucket', $null } -ParameterFilter { $app -eq 'anotherbucket/depends' }
90 | Mock Get-Manifest { 'test', @{ depends = 'anotherbucket/depends' }, 'bucket', $null }
91 | Get-Dependency -AppName 'bucket/test' -Architecture '32bit' | Should -Be @('anotherbucket/depends', 'bucket/test')
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/test/Scoop-GetOpts.Tests.ps1:
--------------------------------------------------------------------------------
1 | BeforeAll {
2 | . "$PSScriptRoot\Scoop-TestLib.ps1"
3 | . "$PSScriptRoot\..\lib\getopt.ps1"
4 | }
5 |
6 | Describe 'getopt' -Tag 'Scoop' {
7 | It 'handle short option with required argument missing' {
8 | $null, $null, $err = getopt '-x' 'x:' ''
9 | $err | Should -Be 'Option -x requires an argument.'
10 |
11 | $null, $null, $err = getopt '-xy' 'x:y' ''
12 | $err | Should -Be 'Option -x requires an argument.'
13 | }
14 |
15 | It 'handle long option with required argument missing' {
16 | $null, $null, $err = getopt '--arb' '' 'arb='
17 | $err | Should -Be 'Option --arb requires an argument.'
18 | }
19 |
20 | It 'handle space in quote' {
21 | $opt, $rem, $err = getopt '-x', 'space arg' 'x:' ''
22 | $err | Should -BeNullOrEmpty
23 | $opt.x | Should -Be 'space arg'
24 | }
25 |
26 | It 'handle unrecognized short option' {
27 | $null, $null, $err = getopt '-az' 'a' ''
28 | $err | Should -Be 'Option -z not recognized.'
29 | }
30 |
31 | It 'handle unrecognized long option' {
32 | $null, $null, $err = getopt '--non-exist' '' ''
33 | $err | Should -Be 'Option --non-exist not recognized.'
34 |
35 | $null, $null, $err = getopt '--global', '--another' 'abc:de:' 'global', 'one'
36 | $err | Should -Be 'Option --another not recognized.'
37 | }
38 |
39 | It 'remaining args returned' {
40 | $opt, $rem, $err = getopt '-g', 'rem' 'g' ''
41 | $err | Should -BeNullOrEmpty
42 | $opt.g | Should -BeTrue
43 | $rem | Should -Not -BeNullOrEmpty
44 | $rem.length | Should -Be 1
45 | $rem[0] | Should -Be 'rem'
46 | }
47 |
48 | It 'get a long flag and a short option with argument' {
49 | $a = '--global -a 32bit test' -split ' '
50 | $opt, $rem, $err = getopt $a 'ga:' 'global', 'arch='
51 |
52 | $err | Should -BeNullOrEmpty
53 | $opt.global | Should -BeTrue
54 | $opt.a | Should -Be '32bit'
55 | }
56 |
57 | It 'handles regex characters' {
58 | $a = '-?'
59 | { $opt, $rem, $err = getopt $a 'ga:' 'global' 'arch=' } | Should -Not -Throw
60 | { $null, $null, $null = getopt $a '?:' 'help' | Should -Not -Throw }
61 | }
62 |
63 | It 'handles short option without required argument' {
64 | $null, $null, $err = getopt '-x' 'x' ''
65 | $err | Should -BeNullOrEmpty
66 | }
67 |
68 | It 'handles long option without required argument' {
69 | $opt, $null, $err = getopt '--long-arg' '' 'long-arg'
70 | $err | Should -BeNullOrEmpty
71 | $opt.'long-arg' | Should -BeTrue
72 | }
73 |
74 | It 'handles long option with required argument' {
75 | $opt, $null, $err = getopt '--long-arg', 'test' '' 'long-arg='
76 | $err | Should -BeNullOrEmpty
77 | $opt.'long-arg' | Should -Be 'test'
78 | }
79 |
80 | It 'handles the option terminator' {
81 | $opt, $rem, $err = getopt '--long-arg', '--' '' 'long-arg'
82 | $err | Should -BeNullOrEmpty
83 | $opt.'long-arg' | Should -BeTrue
84 | $rem[0] | Should -BeNullOrEmpty
85 | $opt, $rem, $err = getopt '--long-arg', '--', '-x', '-y' 'xy' 'long-arg'
86 | $err | Should -BeNullOrEmpty
87 | $opt.'long-arg' | Should -BeTrue
88 | $opt.'x' | Should -BeNullOrEmpty
89 | $opt.'y' | Should -BeNullOrEmpty
90 | $rem[0] | Should -Be '-x'
91 | $rem[1] | Should -Be '-y'
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/test/Scoop-Install.Tests.ps1:
--------------------------------------------------------------------------------
1 | BeforeAll {
2 | . "$PSScriptRoot\Scoop-TestLib.ps1"
3 | . "$PSScriptRoot\..\lib\core.ps1"
4 | . "$PSScriptRoot\..\lib\system.ps1"
5 | . "$PSScriptRoot\..\lib\manifest.ps1"
6 | . "$PSScriptRoot\..\lib\install.ps1"
7 | }
8 |
9 | Describe 'appname_from_url' -Tag 'Scoop' {
10 | It 'should extract the correct name' {
11 | appname_from_url 'https://example.org/directory/foobar.json' | Should -Be 'foobar'
12 | }
13 | }
14 |
15 | Describe 'url_filename' -Tag 'Scoop' {
16 | It 'should extract the real filename from an url' {
17 | url_filename 'http://example.org/foo.txt' | Should -Be 'foo.txt'
18 | url_filename 'http://example.org/foo.txt?var=123' | Should -Be 'foo.txt'
19 | }
20 |
21 | It 'can be tricked with a hash to override the real filename' {
22 | url_filename 'http://example.org/foo-v2.zip#/foo.zip' | Should -Be 'foo.zip'
23 | }
24 | }
25 |
26 | Describe 'url_remote_filename' -Tag 'Scoop' {
27 | It 'should extract the real filename from an url' {
28 | url_remote_filename 'http://example.org/foo.txt' | Should -Be 'foo.txt'
29 | url_remote_filename 'http://example.org/foo.txt?var=123' | Should -Be 'foo.txt'
30 | }
31 |
32 | It 'can not be tricked with a hash to override the real filename' {
33 | url_remote_filename 'http://example.org/foo-v2.zip#/foo.zip' | Should -Be 'foo-v2.zip'
34 | }
35 | }
36 |
37 | Describe 'is_in_dir' -Tag 'Scoop', 'Windows' {
38 | It 'should work correctly' {
39 | is_in_dir 'C:\test' 'C:\foo' | Should -BeFalse
40 | is_in_dir 'C:\test' 'C:\test\foo\baz.zip' | Should -BeTrue
41 | is_in_dir "$PSScriptRoot\..\" "$PSScriptRoot" | Should -BeFalse
42 | }
43 | }
44 |
45 | Describe 'env add and remove path' -Tag 'Scoop', 'Windows' {
46 | BeforeAll {
47 | # test data
48 | $manifest = @{
49 | 'env_add_path' = @('foo', 'bar', '.', '..')
50 | }
51 | $testdir = Join-Path $PSScriptRoot 'path-test-directory'
52 | $global = $false
53 | }
54 |
55 | It 'should concat the correct path' {
56 | Mock Add-Path {}
57 | Mock Remove-Path {}
58 |
59 | # adding
60 | env_add_path $manifest $testdir $global
61 | Should -Invoke -CommandName Add-Path -Times 1 -ParameterFilter { $Path -like "$testdir\foo" }
62 | Should -Invoke -CommandName Add-Path -Times 1 -ParameterFilter { $Path -like "$testdir\bar" }
63 | Should -Invoke -CommandName Add-Path -Times 1 -ParameterFilter { $Path -like $testdir }
64 | Should -Invoke -CommandName Add-Path -Times 0 -ParameterFilter { $Path -like $PSScriptRoot }
65 |
66 | env_rm_path $manifest $testdir $global
67 | Should -Invoke -CommandName Remove-Path -Times 1 -ParameterFilter { $Path -like "$testdir\foo" }
68 | Should -Invoke -CommandName Remove-Path -Times 1 -ParameterFilter { $Path -like "$testdir\bar" }
69 | Should -Invoke -CommandName Remove-Path -Times 1 -ParameterFilter { $Path -like $testdir }
70 | Should -Invoke -CommandName Remove-Path -Times 0 -ParameterFilter { $Path -like $PSScriptRoot }
71 | }
72 | }
73 |
74 | Describe 'shim_def' -Tag 'Scoop' {
75 | It 'should use strings correctly' {
76 | $target, $name, $shimArgs = shim_def 'command.exe'
77 | $target | Should -Be 'command.exe'
78 | $name | Should -Be 'command'
79 | $shimArgs | Should -BeNullOrEmpty
80 | }
81 |
82 | It 'should expand the array correctly' {
83 | $target, $name, $shimArgs = shim_def @('foo.exe', 'bar')
84 | $target | Should -Be 'foo.exe'
85 | $name | Should -Be 'bar'
86 | $shimArgs | Should -BeNullOrEmpty
87 |
88 | $target, $name, $shimArgs = shim_def @('foo.exe', 'bar', '--test')
89 | $target | Should -Be 'foo.exe'
90 | $name | Should -Be 'bar'
91 | $shimArgs | Should -Be '--test'
92 | }
93 | }
94 |
95 | Describe 'persist_def' -Tag 'Scoop' {
96 | It 'parses string correctly' {
97 | $source, $target = persist_def 'test'
98 | $source | Should -Be 'test'
99 | $target | Should -Be 'test'
100 | }
101 |
102 | It 'should handle sub-folder' {
103 | $source, $target = persist_def 'foo/bar'
104 | $source | Should -Be 'foo/bar'
105 | $target | Should -Be 'foo/bar'
106 | }
107 |
108 | It 'should handle arrays' {
109 | # both specified
110 | $source, $target = persist_def @('foo', 'bar')
111 | $source | Should -Be 'foo'
112 | $target | Should -Be 'bar'
113 |
114 | # only first specified
115 | $source, $target = persist_def @('foo')
116 | $source | Should -Be 'foo'
117 | $target | Should -Be 'foo'
118 |
119 | # null value specified
120 | $source, $target = persist_def @('foo', $null)
121 | $source | Should -Be 'foo'
122 | $target | Should -Be 'foo'
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/test/Scoop-Manifest.Tests.ps1:
--------------------------------------------------------------------------------
1 | BeforeAll {
2 | . "$PSScriptRoot\..\lib\json.ps1"
3 | . "$PSScriptRoot\..\lib\manifest.ps1"
4 | }
5 |
6 | Describe 'JSON parse and beautify' -Tag 'Scoop' {
7 | Context 'Parse JSON' {
8 | It 'success with valid json' {
9 | { parse_json "$PSScriptRoot\fixtures\manifest\wget.json" } | Should -Not -Throw
10 | }
11 | It 'fails with invalid json' {
12 | { parse_json "$PSScriptRoot\fixtures\manifest\broken_wget.json" } | Should -Throw
13 | }
14 | }
15 | Context 'Beautify JSON' {
16 | BeforeDiscovery {
17 | $manifests = (Get-ChildItem "$PSScriptRoot\fixtures\format\formatted" -File -Filter '*.json').Name
18 | }
19 | BeforeAll {
20 | $format = "$PSScriptRoot\fixtures\format"
21 | }
22 | It '<_>' -ForEach $manifests {
23 | $pretty_json = (parse_json "$format\unformatted\$_") | ConvertToPrettyJson
24 | $correct = (Get-Content "$format\formatted\$_") -join "`r`n"
25 | $correct.CompareTo($pretty_json) | Should -Be 0
26 | }
27 | }
28 | }
29 |
30 | Describe 'Handle ARM64 and correctly fallback' -Tag 'Scoop' {
31 | It 'Should return "arm64" if supported' {
32 | $manifest1 = @{ url = 'test'; architecture = @{ 'arm64' = @{ pre_install = 'test' } } }
33 | $manifest2 = @{ url = 'test'; pre_install = "'arm64'" }
34 | $manifest3 = @{ architecture = @{ 'arm64' = @{ url = 'test' } } }
35 | Get-SupportedArchitecture $manifest1 'arm64' | Should -Be 'arm64'
36 | Get-SupportedArchitecture $manifest2 'arm64' | Should -Be 'arm64'
37 | Get-SupportedArchitecture $manifest3 'arm64' | Should -Be 'arm64'
38 | }
39 | It 'Should return "64bit" if unsupported on Windows 11' {
40 | $WindowsBuild = 22000
41 | $manifest1 = @{ url = 'test' }
42 | $manifest2 = @{ architecture = @{ '64bit' = @{ url = 'test' } } }
43 | Get-SupportedArchitecture $manifest1 'arm64' | Should -Be '64bit'
44 | Get-SupportedArchitecture $manifest2 'arm64' | Should -Be '64bit'
45 | }
46 | It 'Should return "32bit" if unsupported on Windows 10' {
47 | $WindowsBuild = 19044
48 | $manifest2 = @{ url = 'test' }
49 | $manifest1 = @{ url = 'test'; architecture = @{ '64bit' = @{ pre_install = 'test' } } }
50 | $manifest3 = @{ architecture = @{ '64bit' = @{ url = 'test' } } }
51 | Get-SupportedArchitecture $manifest1 'arm64' | Should -Be '32bit'
52 | Get-SupportedArchitecture $manifest2 'arm64' | Should -Be '32bit'
53 | Get-SupportedArchitecture $manifest3 'arm64' | Should -BeNullOrEmpty
54 | }
55 | }
56 |
57 | Describe 'Manifest Validator' -Tag 'Validator' {
58 | # Could not use backslash '\' in Linux/macOS for .NET object 'Scoop.Validator'
59 | BeforeAll {
60 | Add-Type -Path "$PSScriptRoot\..\supporting\validator\bin\Scoop.Validator.dll"
61 | $schema = "$PSScriptRoot/../schema.json"
62 | }
63 |
64 | It 'Scoop.Validator is available' {
65 | ([System.Management.Automation.PSTypeName]'Scoop.Validator').Type | Should -Be 'Scoop.Validator'
66 | }
67 | It 'fails with broken schema' {
68 | $validator = New-Object Scoop.Validator("$PSScriptRoot/fixtures/manifest/broken_schema.json", $true)
69 | $validator.Validate("$PSScriptRoot/fixtures/manifest/wget.json") | Should -BeFalse
70 | $validator.Errors.Count | Should -Be 1
71 | $validator.Errors | Select-Object -First 1 | Should -Match 'broken_schema.*(line 6).*(position 4)'
72 | }
73 | It 'fails with broken manifest' {
74 | $validator = New-Object Scoop.Validator($schema, $true)
75 | $validator.Validate("$PSScriptRoot/fixtures/manifest/broken_wget.json") | Should -BeFalse
76 | $validator.Errors.Count | Should -Be 1
77 | $validator.Errors | Select-Object -First 1 | Should -Match 'broken_wget.*(line 5).*(position 4)'
78 | }
79 | It 'fails with invalid manifest' {
80 | $validator = New-Object Scoop.Validator($schema, $true)
81 | $validator.Validate("$PSScriptRoot/fixtures/manifest/invalid_wget.json") | Should -BeFalse
82 | $validator.Errors.Count | Should -Be 16
83 | $validator.Errors | Select-Object -First 1 | Should -Match "Property 'randomproperty' has not been defined and the schema does not allow additional properties\."
84 | $validator.Errors | Select-Object -Last 1 | Should -Match 'Required properties are missing from object: version\.'
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/test/Scoop-TestLib.ps1:
--------------------------------------------------------------------------------
1 | # copies fixtures to a working directory
2 | function setup_working($name) {
3 | $fixtures = "$PSScriptRoot\fixtures\$name"
4 | if (!(Test-Path $fixtures)) {
5 | Write-Host "couldn't find fixtures for $name at $fixtures" -f red
6 | exit 1
7 | }
8 |
9 | # reset working dir
10 | $working_dir = "$([IO.Path]::GetTempPath())ScoopTestFixtures\$name"
11 |
12 | if (Test-Path $working_dir) {
13 | Remove-Item -Recurse -Force $working_dir
14 | }
15 |
16 | # set up
17 | Copy-Item $fixtures -Destination $working_dir -Recurse
18 |
19 | return $working_dir
20 | }
21 |
--------------------------------------------------------------------------------
/test/Scoop-Versions.Tests.ps1:
--------------------------------------------------------------------------------
1 | BeforeAll {
2 | . "$PSScriptRoot\Scoop-TestLib.ps1"
3 | . "$PSScriptRoot\..\lib\versions.ps1"
4 | }
5 |
6 | Describe 'versions comparison' -Tag 'Scoop' {
7 | Context 'semver compliant versions' {
8 | It 'handles major.minor.patch progressing' {
9 | Compare-Version '0.1.0' '0.1.1' | Should -Be 1
10 | Compare-Version '0.1.1' '0.2.0' | Should -Be 1
11 | Compare-Version '0.2.0' '1.0.0' | Should -Be 1
12 | }
13 |
14 | It 'handles pre-release versioning progression' {
15 | Compare-Version '0.4.0' '0.5.0-alpha.1' | Should -Be 1
16 | Compare-Version '0.5.0-alpha.1' '0.5.0-alpha.2' | Should -Be 1
17 | Compare-Version '0.5.0-alpha.2' '0.5.0-alpha.10' | Should -Be 1
18 | Compare-Version '0.5.0-alpha.10' '0.5.0-beta' | Should -Be 1
19 | Compare-Version '0.5.0-beta' '0.5.0-alpha.10' | Should -Be -1
20 | Compare-Version '0.5.0-beta' '0.5.0-beta.0' | Should -Be 1
21 | }
22 |
23 | It 'handles the pre-release tags in an alphabetic order' {
24 | Compare-Version '0.5.0-rc.1' '0.5.0-z' | Should -Be 1
25 | Compare-Version '0.5.0-rc.1' '0.5.0-howdy' | Should -Be -1
26 | Compare-Version '0.5.0-howdy' '0.5.0-rc.1' | Should -Be 1
27 | }
28 | }
29 |
30 | Context 'semver semi-compliant versions' {
31 | It 'handles Windows-styled major.minor.patch.build progression' {
32 | Compare-Version '0.0.0.0' '0.0.0.1' | Should -Be 1
33 | Compare-Version '0.0.0.1' '0.0.0.2' | Should -Be 1
34 | Compare-Version '0.0.0.2' '0.0.1.0' | Should -Be 1
35 | Compare-Version '0.0.1.0' '0.0.1.1' | Should -Be 1
36 | Compare-Version '0.0.1.1' '0.0.1.2' | Should -Be 1
37 | Compare-Version '0.0.1.2' '0.0.2.0' | Should -Be 1
38 | Compare-Version '0.0.2.0' '0.1.0.0' | Should -Be 1
39 | Compare-Version '0.1.0.0' '0.1.0.1' | Should -Be 1
40 | Compare-Version '0.1.0.1' '0.1.0.2' | Should -Be 1
41 | Compare-Version '0.1.0.2' '0.1.1.0' | Should -Be 1
42 | Compare-Version '0.1.1.0' '0.1.1.1' | Should -Be 1
43 | Compare-Version '0.1.1.1' '0.1.1.2' | Should -Be 1
44 | Compare-Version '0.1.1.2' '0.2.0.0' | Should -Be 1
45 | Compare-Version '0.2.0.0' '1.0.0.0' | Should -Be 1
46 | }
47 |
48 | It 'handles partial semver version differences' {
49 | Compare-Version '1' '1.1' | Should -Be 1
50 | Compare-Version '1' '1.0' | Should -Be 1
51 | Compare-Version '1.1.0.0' '1.1' | Should -Be -1
52 | Compare-Version '1.4' '1.3.0' | Should -Be -1
53 | Compare-Version '1.4' '1.3.255.255' | Should -Be -1
54 | Compare-Version '1.4' '1.4.4' | Should -Be 1
55 | Compare-Version '1.1.1_8' '1.1.1' | Should -Be -1
56 | Compare-Version '1.1.1_8' '1.1.1_9' | Should -Be 1
57 | Compare-Version '1.1.1_10' '1.1.1_9' | Should -Be -1
58 | Compare-Version '1.1.1b' '1.1.1a' | Should -Be -1
59 | Compare-Version '1.1.1a' '1.1.1b' | Should -Be 1
60 | Compare-Version '1.1a2' '1.1a3' | Should -Be 1
61 | Compare-Version '1.1.1a10' '1.1.1b1' | Should -Be 1
62 | }
63 |
64 | It 'handles dash-style versions' {
65 | Compare-Version '1.8.9' '1.8.5-1' | Should -Be -1
66 | Compare-Version '7.0.4-9' '7.0.4-10' | Should -Be 1
67 | Compare-Version '7.0.4-9' '7.0.4-8' | Should -Be -1
68 | Compare-Version '2019-01-01' '2019-01-02' | Should -Be 1
69 | Compare-Version '2019-01-02' '2019-01-01' | Should -Be -1
70 | Compare-Version '2018-01-01' '2019-01-01' | Should -Be 1
71 | Compare-Version '2019-01-01' '2018-01-01' | Should -Be -1
72 | }
73 | It 'handles post-release tagging ("+")' {
74 | Compare-Version '1' '1+hotfix.0' | Should -Be 1
75 | Compare-Version '1.0.0' '1.0.0+hotfix.0' | Should -Be 1
76 | Compare-Version '1.0.0+hotfix.0' '1.0.0+hotfix.1' | Should -Be 1
77 | Compare-Version '1.0.0+hotfix.1' '1.0.1' | Should -Be 1
78 | Compare-Version '1.0.0+1.1' '1.0.0+1' | Should -Be -1
79 | }
80 | }
81 |
82 | Context 'other misc versions' {
83 | It 'handles plain text string' {
84 | Compare-Version 'latest' '20150405' | Should -Be -1
85 | Compare-Version '0.5alpha' '0.5' | Should -Be 1
86 | Compare-Version '0.5' '0.5Beta' | Should -Be -1
87 | Compare-Version '0.4' '0.5Beta' | Should -Be 1
88 | }
89 |
90 | It 'handles empty string' {
91 | Compare-Version '7.0.4-9' '' | Should -Be -1
92 | }
93 |
94 | It 'handles equal versions' {
95 | function get_config { $null }
96 | Compare-Version '12.0' '12.0' | Should -Be 0
97 | Compare-Version '7.0.4-9' '7.0.4-9' | Should -Be 0
98 | Compare-Version 'nightly-20190801' 'nightly' | Should -Be 0
99 | Compare-Version 'nightly-20190801' 'nightly-20200801' | Should -Be 0
100 | }
101 |
102 | It "handles nightly versions with 'update_nightly'" {
103 | function get_config { $true }
104 | Mock Get-Date { '20200801' }
105 | Compare-Version 'nightly-20200801' 'nightly' | Should -Be 0
106 | Compare-Version 'nightly-20200730' 'nightly' | Should -Be 1
107 | Compare-Version 'nightly-20200730' 'nightly-20200801' | Should -Be 1
108 | Compare-Version 'nightly-20200802' 'nightly' | Should -Be -1
109 | Compare-Version 'nightly-20200802' 'nightly-20200801' | Should -Be -1
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/test/bin/init.ps1:
--------------------------------------------------------------------------------
1 | #Requires -Version 5.1
2 | Write-Output "PowerShell: $($PSVersionTable.PSVersion)"
3 | Write-Output 'Check and install testsuite dependencies ...'
4 | if (Get-InstalledModule -Name Pester -MinimumVersion 5.2 -MaximumVersion 5.99 -ErrorAction SilentlyContinue) {
5 | Write-Output 'Pester 5 is already installed.'
6 | } else {
7 | Write-Output 'Installing Pester 5 ...'
8 | Install-Module -Repository PSGallery -Scope CurrentUser -Force -Name Pester -MinimumVersion 5.2 -MaximumVersion 5.99 -SkipPublisherCheck
9 | }
10 | if (Get-InstalledModule -Name PSScriptAnalyzer -MinimumVersion 1.17 -ErrorAction SilentlyContinue) {
11 | Write-Output 'PSScriptAnalyzer is already installed.'
12 | } else {
13 | Write-Output 'Installing PSScriptAnalyzer ...'
14 | Install-Module -Repository PSGallery -Scope CurrentUser -Force -Name PSScriptAnalyzer -SkipPublisherCheck
15 | }
16 | if (Get-InstalledModule -Name BuildHelpers -MinimumVersion 2.0 -ErrorAction SilentlyContinue) {
17 | Write-Output 'BuildHelpers is already installed.'
18 | } else {
19 | Write-Output 'Installing BuildHelpers ...'
20 | Install-Module -Repository PSGallery -Scope CurrentUser -Force -Name BuildHelpers -SkipPublisherCheck
21 | }
22 |
--------------------------------------------------------------------------------
/test/bin/test.ps1:
--------------------------------------------------------------------------------
1 | #Requires -Version 5.1
2 | #Requires -Modules @{ ModuleName = 'BuildHelpers'; ModuleVersion = '2.0.1' }
3 | #Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.2.0' }
4 | #Requires -Modules @{ ModuleName = 'PSScriptAnalyzer'; ModuleVersion = '1.17.1' }
5 | param(
6 | [String] $TestPath = (Convert-Path "$PSScriptRoot\..")
7 | )
8 |
9 | $pesterConfig = New-PesterConfiguration -Hashtable @{
10 | Run = @{
11 | Path = $TestPath
12 | PassThru = $true
13 | }
14 | Output = @{
15 | Verbosity = 'Detailed'
16 | }
17 | }
18 | $excludes = @()
19 |
20 | if ($IsLinux -or $IsMacOS) {
21 | Write-Warning 'Skipping Windows-only tests on Linux/macOS'
22 | $excludes += 'Windows'
23 | }
24 |
25 | if ($env:CI -eq $true) {
26 | Write-Host "Load 'BuildHelpers' environment variables ..."
27 | Set-BuildEnvironment -Force
28 |
29 | # Check if tests are called from the Core itself, if so, adding excludes
30 | if ($TestPath -eq (Convert-Path "$PSScriptRoot\..")) {
31 | if ($env:BHCommitMessage -match '!linter') {
32 | Write-Warning "Skipping code linting per commit flag '!linter'"
33 | $excludes += 'Linter'
34 | }
35 |
36 | $changedScripts = (Get-GitChangedFile -Include '*.ps1', '*.psd1', '*.psm1' -Commit $env:BHCommitHash)
37 | if (!$changedScripts) {
38 | Write-Warning "Skipping tests and code linting for PowerShell scripts because they didn't change"
39 | $excludes += 'Linter'
40 | $excludes += 'Scoop'
41 | }
42 |
43 | if (!($changedScripts -like '*decompress.ps1') -and !($changedScripts -like '*Decompress.Tests.ps1')) {
44 | Write-Warning "Skipping tests and code linting for decompress.ps1 files because it didn't change"
45 | $excludes += 'Decompress'
46 | }
47 |
48 | if ('Decompress' -notin $excludes -and 'Windows' -notin $excludes) {
49 | Write-Host 'Install decompress dependencies ...'
50 |
51 | Write-Host (7z.exe | Select-String -Pattern '7-Zip').ToString()
52 |
53 | $env:SCOOP_HELPERS_PATH = 'C:\projects\helpers'
54 | if (!(Test-Path $env:SCOOP_HELPERS_PATH)) {
55 | New-Item -ItemType Directory -Path $env:SCOOP_HELPERS_PATH | Out-Null
56 | }
57 |
58 | $env:SCOOP_LESSMSI_PATH = "$env:SCOOP_HELPERS_PATH\lessmsi\lessmsi.exe"
59 | if (!(Test-Path $env:SCOOP_LESSMSI_PATH)) {
60 | $source = 'https://github.com/activescott/lessmsi/releases/download/v1.10.0/lessmsi-v1.10.0.zip'
61 | $destination = "$env:SCOOP_HELPERS_PATH\lessmsi.zip"
62 | Invoke-WebRequest -Uri $source -OutFile $destination
63 | & 7z.exe x "$env:SCOOP_HELPERS_PATH\lessmsi.zip" -o"$env:SCOOP_HELPERS_PATH\lessmsi" -y | Out-Null
64 | }
65 |
66 | $env:SCOOP_INNOUNP_PATH = "$env:SCOOP_HELPERS_PATH\innounp\innounp.exe"
67 | if (!(Test-Path $env:SCOOP_INNOUNP_PATH)) {
68 | $source = 'https://raw.githubusercontent.com/ScoopInstaller/Binary/master/innounp/innounp050.rar'
69 | $destination = "$env:SCOOP_HELPERS_PATH\innounp.rar"
70 | Invoke-WebRequest -Uri $source -OutFile $destination
71 | & 7z.exe x "$env:SCOOP_HELPERS_PATH\innounp.rar" -o"$env:SCOOP_HELPERS_PATH\innounp" -y | Out-Null
72 | }
73 | }
74 | }
75 |
76 | # Display CI environment variables
77 | $buildVariables = (Get-ChildItem -Path 'Env:').Where({ $_.Name -match '^(?:BH|CI(?:_|$)|APPVEYOR|GITHUB_|RUNNER_|SCOOP_)' })
78 | $details = $buildVariables |
79 | Where-Object -FilterScript { $_.Name -notmatch 'EMAIL' } |
80 | Sort-Object -Property 'Name' |
81 | Format-Table -AutoSize -Property 'Name', 'Value' |
82 | Out-String
83 | Write-Host 'CI variables:'
84 | Write-Host $details -ForegroundColor DarkGray
85 | }
86 |
87 | if ($excludes.Length -gt 0) {
88 | $pesterConfig.Filter.ExcludeTag = $excludes
89 | }
90 |
91 | if ($env:BHBuildSystem -eq 'AppVeyor') {
92 | # AppVeyor
93 | $resultsXml = "$PSScriptRoot\TestResults.xml"
94 | $pesterConfig.TestResult.Enabled = $true
95 | $pesterConfig.TestResult.OutputPath = $resultsXml
96 | $result = Invoke-Pester -Configuration $pesterConfig
97 | Add-TestResultToAppveyor -TestFile $resultsXml
98 | } else {
99 | # GitHub Actions / Local
100 | $result = Invoke-Pester -Configuration $pesterConfig
101 | }
102 |
103 | exit $result.FailedCount
104 |
--------------------------------------------------------------------------------
/test/fixtures/decompress/TestCases.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ScoopInstaller/Scoop/859d1db51bcc840903d5280567846ae2f7207ca2/test/fixtures/decompress/TestCases.zip
--------------------------------------------------------------------------------
/test/fixtures/format/formatted/1-easy.json:
--------------------------------------------------------------------------------
1 | {
2 | "bin": "single"
3 | }
4 |
--------------------------------------------------------------------------------
/test/fixtures/format/formatted/2-whitespaces-mess.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.5.18",
3 | "url": "https://whatever",
4 | "hash": "whatever",
5 | "architecture": {
6 | "64bit": {
7 | "installer": {
8 | "script": [
9 | "Do something",
10 | "cosi"
11 | ]
12 | }
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/test/fixtures/format/formatted/3-array-with-single-and-multi.json:
--------------------------------------------------------------------------------
1 | {
2 | "homepage": "http://www.7-zip.org/",
3 | "description": "A multi-format file archiver with high compression ratios",
4 | "license": {
5 | "identifier": "LGPL-2.0-only,BSD-3-Clause",
6 | "url": "https://www.7-zip.org/license.txt"
7 | },
8 | "version": "18.05",
9 | "architecture": {
10 | "64bit": {
11 | "url": "https://7-zip.org/a/7z1805-x64.msi",
12 | "hash": "898c1ca0015183fe2ba7d55cacf0a1dea35e873bf3f8090f362a6288c6ef08d7"
13 | },
14 | "32bit": {
15 | "url": "https://7-zip.org/a/7z1805.msi",
16 | "hash": "c554238bee18a03d736525e06d9258c9ecf7f64ead7c6b0d1eb04db2c0de30d0"
17 | }
18 | },
19 | "extract_dir": "Files/7-Zip",
20 | "bin": [
21 | "single",
22 | [
23 | "7z.exe",
24 | "cosi"
25 | ],
26 | [
27 | "7z.exe",
28 | "cosi",
29 | "param",
30 | "icon"
31 | ],
32 | [
33 | "7z.exe",
34 | "empty",
35 | "",
36 | ""
37 | ],
38 | "singtwo"
39 | ],
40 | "checkver": "Download 7-Zip ([\\d.]+)",
41 | "autoupdate": {
42 | "architecture": {
43 | "64bit": {
44 | "url": "https://7-zip.org/a/7z$cleanVersion-x64.msi"
45 | },
46 | "32bit": {
47 | "url": "https://7-zip.org/a/7z$cleanVersion.msi"
48 | }
49 | }
50 | },
51 | "shortcuts": [
52 | [
53 | "7zFM.exe",
54 | "7-Zip"
55 | ],
56 | [
57 | "name with spaces.exe",
58 | "Shortcut with spaces in name"
59 | ]
60 | ]
61 | }
62 |
--------------------------------------------------------------------------------
/test/fixtures/format/formatted/4-script-block.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.0.6",
3 | "description": "Rambox Pro. Free, Open Source and Cross Platform messaging and emailing app that combines common web applications into one.",
4 | "homepage": "https://rambox.pro/",
5 | "url": "https://github.com/ramboxapp/download/releases/download/v1.0.6/RamboxPro-1.0.6-win.exe#/cosi.7z",
6 | "hash": "sha512:f4a1b5e12ae15c9a1339fef56b0522b6619d6c23b0ab806f128841c2ba7ce9d9c997fea81f5bc4a24988aed672a4415ff353542535dc7869b5e496f2f1e1efff",
7 | "extract_dir": "\\$PLUGINSDIR",
8 | "pre_install": "Get-ChildItem \"$dir\" -Exclude 'app-64.7z', 'app-32.7z' | Remove-Item -Force -Recurse",
9 | "architecture": {
10 | "64bit": {
11 | "installer": {
12 | "script": "Expand-7zipArchive \"$dir\\app-64.7z\" \"$dir\""
13 | }
14 | },
15 | "32bit": {
16 | "installer": {
17 | "script": "Expand-7zipArchive \"$dir\\app-32.7z\" \"$dir\""
18 | }
19 | }
20 | },
21 | "post_install": "Remove-Item \"$dir\\app-64.7z\", \"$dir\\app-32.7z\"",
22 | "shortcuts": [
23 | [
24 | "RamboxPro.exe",
25 | "RamboxPro"
26 | ]
27 | ],
28 | "checkver": {
29 | "github": "https://github.com/ramboxapp/download/"
30 | },
31 | "autoupdate": {
32 | "url": "https://github.com/ramboxapp/download/releases/download/v$version/RamboxPro-$version-win.exe#/cosi.7z",
33 | "hash": {
34 | "url": "https://github.com/ramboxapp/download/releases/download/v$version/latest.yml",
35 | "find": "sha512:\\s+(.*)"
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/test/fixtures/format/unformatted/1-easy.json:
--------------------------------------------------------------------------------
1 | { "bin": ["single"]}
2 |
--------------------------------------------------------------------------------
/test/fixtures/format/unformatted/2-whitespaces-mess.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.5.18",
3 | "url": "https://whatever",
4 |
5 | "hash": "whatever",
6 | "architecture": {
7 | "64bit": { "installer": {
8 |
9 | "script": [
10 | "Do something" , "cosi"
11 | ]
12 | }
13 | }
14 | }
15 | }
16 |
17 |
--------------------------------------------------------------------------------
/test/fixtures/format/unformatted/3-array-with-single-and-multi.json:
--------------------------------------------------------------------------------
1 | {
2 | "homepage": "http://www.7-zip.org/",
3 | "description": "A multi-format file archiver with high compression ratios",
4 | "license": {
5 | "identifier": "LGPL-2.0-only,BSD-3-Clause",
6 | "url": "https://www.7-zip.org/license.txt"
7 | },
8 | "version": "18.05",
9 | "architecture": {
10 | "64bit": {
11 | "url": "https://7-zip.org/a/7z1805-x64.msi",
12 | "hash": [
13 | "898c1ca0015183fe2ba7d55cacf0a1dea35e873bf3f8090f362a6288c6ef08d7"
14 | ]
15 | },
16 | "32bit": {
17 | "url": "https://7-zip.org/a/7z1805.msi",
18 | "hash": "c554238bee18a03d736525e06d9258c9ecf7f64ead7c6b0d1eb04db2c0de30d0"
19 | }
20 | },
21 | "extract_dir": "Files/7-Zip",
22 | "bin": [
23 | [
24 | "single"
25 | ],
26 | [
27 | "7z.exe",
28 | "cosi"
29 | ],
30 | [
31 | "7z.exe",
32 | "cosi",
33 | "param",
34 | "icon"
35 | ],
36 | [
37 | "7z.exe",
38 | "empty",
39 | "",
40 | ""
41 | ],
42 | [
43 | "singtwo"
44 | ]
45 | ],
46 | "checkver": "Download 7-Zip ([\\d.]+)",
47 | "autoupdate": {
48 | "architecture": {
49 | "64bit": {
50 | "url": "https://7-zip.org/a/7z$cleanVersion-x64.msi"
51 | }, "32bit": {
52 | "url": "https://7-zip.org/a/7z$cleanVersion.msi"
53 | }
54 | }
55 | },
56 | "shortcuts": [
57 | [ "7zFM.exe",
58 | "7-Zip"
59 | ], ["name with spaces.exe", "Shortcut with spaces in name"] ]}
60 |
--------------------------------------------------------------------------------
/test/fixtures/format/unformatted/4-script-block.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.0.6",
3 | "description": "Rambox Pro. Free, Open Source and Cross Platform messaging and emailing app that combines common web applications into one.",
4 | "homepage": "https://rambox.pro/",
5 | "url": "https://github.com/ramboxapp/download/releases/download/v1.0.6/RamboxPro-1.0.6-win.exe#/cosi.7z",
6 | "hash": "sha512:f4a1b5e12ae15c9a1339fef56b0522b6619d6c23b0ab806f128841c2ba7ce9d9c997fea81f5bc4a24988aed672a4415ff353542535dc7869b5e496f2f1e1efff",
7 | "extract_dir": "\\$PLUGINSDIR",
8 | "pre_install": ["Get-ChildItem \"$dir\" -Exclude 'app-64.7z', 'app-32.7z' | Remove-Item -Force -Recurse"],
9 | "architecture": {
10 | "64bit": {
11 | "installer": {
12 | "script": [
13 | "Expand-7zipArchive \"$dir\\app-64.7z\" \"$dir\""
14 | ]
15 | }
16 | },
17 | "32bit": {
18 | "installer": {
19 | "script": [
20 | "Expand-7zipArchive \"$dir\\app-32.7z\" \"$dir\""
21 | ]
22 | }
23 | }
24 | },
25 | "post_install": ["Remove-Item \"$dir\\app-64.7z\", \"$dir\\app-32.7z\""],
26 | "shortcuts": [
27 | [
28 | "RamboxPro.exe",
29 | "RamboxPro"
30 | ]
31 | ],
32 | "checkver": {
33 | "github": "https://github.com/ramboxapp/download/"
34 | },
35 | "autoupdate": {
36 | "url": "https://github.com/ramboxapp/download/releases/download/v$version/RamboxPro-$version-win.exe#/cosi.7z",
37 | "hash": {
38 | "url": "https://github.com/ramboxapp/download/releases/download/v$version/latest.yml",
39 | "find": "sha512:\\s+(.*)"
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/test/fixtures/is_directory/i_am_a_directory/.gitkeep:
--------------------------------------------------------------------------------
1 | need some content to not fail tests
2 |
--------------------------------------------------------------------------------
/test/fixtures/is_directory/i_am_a_file.txt:
--------------------------------------------------------------------------------
1 | dummy content
2 |
--------------------------------------------------------------------------------
/test/fixtures/manifest/broken_schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "$id": "http://scoop.sh/draft/schema#",
3 | "$schema": "http://scoop.sh/draft/schema#",
4 | "title": "scoop app manifest schema",
5 | "type": "object"
6 | "properties": {
7 | "version": {
8 | "type": "string"
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/test/fixtures/manifest/broken_wget.json:
--------------------------------------------------------------------------------
1 | {
2 | "homepage": "https://eternallybored.org/misc/wget/",
3 | "license": "GPL3",
4 | "version": "1.16.3"
5 | "architecture": {
6 | "64bit": {
7 | "url": [
8 | "https://eternallybored.org/misc/wget/wget-1.16.3-win64.zip",
9 | "http://curl.haxx.se/ca/cacert.pem"
10 | ],
11 | "hash": [
12 | "85e5393ffd473f7bec40b57637fd09b6808df86c06f846b6885b261a8acac8c5",
13 | ""
14 | ]
15 | },
16 | "32bit": {
17 | "url": [
18 | "https://eternallybored.org/misc/wget/wget-1.16.3-win32.zip",
19 | "http://curl.haxx.se/ca/cacert.pem"
20 | ],
21 | "hash": [
22 | "2ef82af3070abfdaf3862baff0bffdcb3c91c8d75e2f02c8720d90adb9d7a8f7",
23 | ""
24 | ]
25 | }
26 | },
27 | "bin": "wget.exe",
28 | "post_install": "\"ca_certificate=$dir\\cacert.pem\" | out-file $dir\\wget.ini -encoding default"
29 | }
30 |
--------------------------------------------------------------------------------
/test/fixtures/manifest/invalid_wget.json:
--------------------------------------------------------------------------------
1 | {
2 | "homepage": "https://eternallybored.org/misc/wget/",
3 | "randomproperty": "should fail",
4 | "license": "GPL3",
5 | "architecture": {
6 | "64bit": {
7 | "url": [
8 | "https://eternallybored.org/misc/wget/wget-$version-win64.zip",
9 | "http://curl.haxx.se/ca/cacert.pem"
10 | ],
11 | "hash": [
12 | "85e5393ffd473f7bec40b57637fd09b6808df86c06f846b6885b261a8acac8c5",
13 | ""
14 | ]
15 | },
16 | "32bit": {
17 | "url": [
18 | "https://eternallybored.org/misc/wget/wget-$version-win32.zip",
19 | "http://curl.haxx.se/ca/cacert.pem"
20 | ],
21 | "hash": [
22 | "2ef82af3070abfdaf3862baff0bffdcb3c91c8d75e2f02c8720d90adb9d7a8f7",
23 | ""
24 | ]
25 | }
26 | },
27 | "bin": "wget.exe",
28 | "post_install": "\"ca_certificate=$dir\\cacert.pem\" | out-file $dir\\wget.ini -encoding default"
29 | }
30 |
--------------------------------------------------------------------------------
/test/fixtures/manifest/wget.json:
--------------------------------------------------------------------------------
1 | {
2 | "homepage": "https://eternallybored.org/misc/wget/",
3 | "license": "GPL3",
4 | "version": "1.16.3",
5 | "architecture": {
6 | "64bit": {
7 | "url": [
8 | "https://eternallybored.org/misc/wget/wget-1.16.3-win64.zip",
9 | "http://curl.haxx.se/ca/cacert.pem"
10 | ],
11 | "hash": [
12 | "85e5393ffd473f7bec40b57637fd09b6808df86c06f846b6885b261a8acac8c5",
13 | ""
14 | ]
15 | },
16 | "32bit": {
17 | "url": [
18 | "https://eternallybored.org/misc/wget/wget-1.16.3-win32.zip",
19 | "http://curl.haxx.se/ca/cacert.pem"
20 | ],
21 | "hash": [
22 | "2ef82af3070abfdaf3862baff0bffdcb3c91c8d75e2f02c8720d90adb9d7a8f7",
23 | ""
24 | ]
25 | }
26 | },
27 | "bin": "wget.exe",
28 | "post_install": "\"ca_certificate=$dir\\cacert.pem\" | out-file $dir\\wget.ini -encoding default"
29 | }
30 |
--------------------------------------------------------------------------------
/test/fixtures/movedir/user with 'quote/_tmp/subdir/test.txt:
--------------------------------------------------------------------------------
1 | this is the one
2 |
--------------------------------------------------------------------------------
/test/fixtures/movedir/user with 'quote/_tmp/test.txt:
--------------------------------------------------------------------------------
1 | testing
2 |
--------------------------------------------------------------------------------
/test/fixtures/movedir/user with space/_tmp/subdir/test.txt:
--------------------------------------------------------------------------------
1 | this is the one
2 |
--------------------------------------------------------------------------------
/test/fixtures/movedir/user with space/_tmp/test.txt:
--------------------------------------------------------------------------------
1 | testing
2 |
--------------------------------------------------------------------------------
/test/fixtures/movedir/user/_tmp/subdir/test.txt:
--------------------------------------------------------------------------------
1 | this is the one
2 |
--------------------------------------------------------------------------------
/test/fixtures/movedir/user/_tmp/test.txt:
--------------------------------------------------------------------------------
1 | testing
2 |
--------------------------------------------------------------------------------
/test/fixtures/shim/shim-test.ps1:
--------------------------------------------------------------------------------
1 | 'Hello, world!'
2 |
--------------------------------------------------------------------------------
/test/fixtures/shim/user with 'quote/shim-test.ps1:
--------------------------------------------------------------------------------
1 | 'Hello, world!'
2 |
--------------------------------------------------------------------------------