Concepts
Apps
Scoop uses the term 'app' to refer to an application or program to be installed. You might also see it used to loosely refer to a manifest that describes how to install an app.
An app is a self-contained, independent unit. It contains one or more executables or scripts. But that's probably too much of an explanation—generally you'll know an app when you see it.
Probably the main reason for the term 'app' is that it only takes 3 keystrokes on 2 keys to type.
Buckets
In Scoop, buckets are collections of apps. Or, to be more specific, a bucket is a Git repository containing JSON app manifests which describe how to install an app.
Scoop has a main bucket which is bundled with Scoop and this is always available as the primary source for installing apps.
By default, when you run scoop install <app>
, it looks in the main bucket, but it's possible to install from other buckets too.
There's an optional extras bucket containing apps that don't quite fit the criteria of the main bucket, but are still good to have. There is also an optional 'versions' bucket containing older versions of some well-known packages.
And Scoop supports adding other buckets. Anyone can set up their own bucket with their own set of apps, and other people can add and install from this bucket—they just need to know the location of the bucket's Git repository.
Known buckets
There is a list of known buckets by the community, those can be seen in buckets.json
, to see the list of known buckets execute:
scoop bucket known
Installing from other buckets
If you want to install from a bucket besides the main one, you need to configure Scoop to know about the bucket. For example, to add the optional 'extras' bucket, run:
scoop bucket add extras
The 'extras' bucket is a special bucket, in that it's "well known", i.e. Scoop already knows where this bucket is so you don't have to specify its location.
Just say the extras bucket wasn't well known, the way you'd add it would be:
scoop bucket add extras https://github.com/lukesampson/scoop-extras.git
That is,
scoop bucket add <name-of-bucket> <location-of-git-repo>
You can run scoop help bucket
for more information on buckets.
Creating your own bucket
Here's an example of one way you might go about creating a new bucket, using GitHub to host it. You don't have to use GitHub though—you can use whatever source control repo you like, or even just a Git repo on your local or network drive.
Create a new GitHub repo called my-bucket
Add an app to your bucket. In a powershell session:
git clone https://github.com/<your-username>/my-bucket cd my-bucket '{ version: "1.0", url: "https://gist.github.com/lukesampson/6446238/raw/hello.ps1", bin: "hello.ps1" }' > hello.json git add . git commit -m "add hello app" git push
Configure Scoop to use your new bucket:
scoop bucket add my-bucket https://github.com/<your-username>/my-bucket
Check that it works:
scoop bucket list # -> you should see 'my-bucket' scoop search hello # -> you should see hello listed under, 'my-bucket bucket:' scoop install hello hello # -> you should see 'Hello, <windows-username>!'
To share your bucket, all you need to do is tell people how to add you bucket, i.e. by running the command in step 3.
App Manifests
An app manifest is a JSON file that describes how to install a program.
A simple example:
{
"version": "1.0",
"url": "https://github.com/lukesampson/cowsay-psh/archive/master.zip",
"extract_dir": "cowsay-psh-master",
"bin": "cowsay.ps1"
}
When this manifest is run with scoop install
it will download the zip file specified by url
, extract the "cowsay-psh-master" directory from the zip file, and then make the cowsay.ps1
script available on your path.
For more examples, see the app manifests in the main Scoop bucket.
Required Properties
version
: The version of the app that this manifest installs.url
: The URL or URLs of files to download. If there's more than one URL, you can use a JSON * array, e.g."url": [ "http://example.org/program.zip", "http://example.org/dependencies.zip" ]
. URLs can be HTTP, HTTPS or FTP.
To change the filename of the downloaded URL, you can append a URL fragment (starting with
#
) to URLs. For example,"http://example.org/program.exe"
->"http://example.org/program.exe#/dl.7z"
Note the fragment must start with
#/
for this to work.In the above example, Scoop will download
program.exe
but save it asdl.7z
, which will then be extracted automatically with 7-Zip. This technique is commonly used in Scoop manifests to bypass executable installers which might have undesirable side-effects like registry changes, files placed outside the install directory, or an admin elevation prompt.Optional Properties
##
: A one-line string, or array of strings, containing comments.architecture
: If the app has 32- and 64-bit versions, architecture can be used to wrap the differences (example]).32bit|64bit
: contains architecture-specific instructions (bin
,checkver
,extract_dir
,hash
,installer
,pre_install
,post_install
,shortcuts
,uninstaller
,url
, andmsi
[msi
is deprecated]).
autoupdate
: Definition of how the manifest can be updated automatically.bin
: A string or array of strings of programs (executables or scripts) to make available on the user's path.- You can also create an alias shim which uses a different name to the real executable and (optionally) passes arguments to the executable. Instead of just using a string for the executable, use e.g:
[ "program.exe", "alias", "--arguments" ]
. See busybox for an example. - However if you declare just one shim like this, you must ensure it's enclosed in an outer array, e.g:
"bin": [ [ "program.exe", "alias" ] ]
. Otherwise it will be read as separate shims.
- You can also create an alias shim which uses a different name to the real executable and (optionally) passes arguments to the executable. Instead of just using a string for the executable, use e.g:
checkver
: App maintainers and developers can use the bin/checkver tool to check for updated versions of apps. Thecheckver
property in a manifest is a regular expression that can be used to match the current stable version of an app from the app's homepage. For an example, see the go manifest. If the homepage doesn't have a reliable indication of the current version, you can also specify a different URL to check—for an example see the ruby manifest.depends
: Runtime dependencies for the app which will be installed automatically. See alsosuggest
(below) for an alternative todepends
.description
: A one line string containing a short description of the program. Don’t include the name of the program, if it’s the same as the app’s filename.env_add_path
: Add this directory to the user's path (or system path if--global
is used). The directory is relative to the install directory and must be inside the install directory.env_set
: Sets one or more environment variables for the user (or system if--global
is used) (example).extract_dir
: Ifurl
points to a compressed file (.zip, .7z, .tar, .gz, .lzma, and .lzh are supported), Scoop will extract just the directory specified from it.hash
: A string or array of strings with a file hash for each URL inurl
. Hashes are SHA256 by default, but you can use SHA512, SHA1 or MD5 by prefixing the hash string with 'sha512:', 'sha1:' or 'md5:'.homepage
: The home page for the program.innosetup
: set to the booleantrue
(without quotes) if the installer is InnoSetup based.installer
|uninstaller
: Instructions for running a non-MSI installer.file
: The installer executable file. Forinstaller
this defaults to the last URL downloaded. Must be specified foruninstaller
.script
: A one-line string, or array of strings, of commands to be executed as an installer/uninstaller instead offile
.args
: An array of arguments to pass to the installer. Optional.keep
:"true"
if the installer should be kept after running (for future uninstallation, as an example). If omitted or set to any other value, the installer will be deleted after running. Seeextras/oraclejdk
for an example. This option will be ignored when used in anuninstaller
directive.
license
: A string or hash of the software license for the program. For well-known licenses, please use the identifier found at spdx For other licenses, use the URL of the license, if available. Otherwise, use “Freeware”, “Proprietary”, “Public Domain”, or “Shareware”, as appropriate. If multiple licenses, seperate licenses with a comma (,). If dual licensed, seperate licenses with a pipe symbol (|).identifier
: The SPDX identifier, or “Freeware”, “Proprietary”, “Public Domain”, or “Shareware”, as appropriateurl
: For non-SPDX licenses, include a link to the license.
notes
: A one-line string, or array of strings, with a message to be displayed after installing the app.persist
A string or array of strings of directories and files to persist inside the data directory for the app. Persistent datapre_install
|post_install
: A one-line string, or array of strings, of the commands to be executed before or after an application is installed. (Available variables:$dir
,$persist_dir
,$version
many more (check thelib/install
script))psmodule
: Install as a PowerShell module in~/scoop/modules
.name
(required forpsmodule
): the name of the module, which should match at least one file in the extracted directory for PowerShell to recognize this as a module.
shortcuts
: Specifies the shortcut values to make available in the startmenu. See sourcetree for an example. The array has to contain a executable/label pair. The third and fourth element are optional.- Path to target file [required]
- Name of the shortcut (supports subdirectories:
<AppsSubDir>\\<AppShortcut>
e.g. sysinternals) [required] - Start parameters [optional]
- Path to icon file [optional]
suggest
: Display a message suggesting optional apps that provide complementary features. See ant for an example.["Feature Name"] = [ "app1", "app2"... ]
e.g."JDK": [ "extras/oraclejdk", "openjdk" ]
If any of the apps suggested for the feature are already installed, the feature will be treated as 'fulfilled' and the user won't see any suggestions.
Undocumented Properties
cookie
: only found here
Deprecated Properties
_comment
: A one-line string, or array of strings, containing comments. Use##
instead.extract_to
: No longer used.msi
(deprecated): Settings for running an MSI installer
This property is deprecated and support will be removed in a future version of Scoop. The new method is to treat .msi files just like a .zip and extract the files from it without running the full install. You can use the new method simply by not including this
msi
property in your manifest.code
required: the product code GUID for the MSI installersilent
: should normally betrue
to try to install without popups and UAC prompts
Creating an App Manifest
If you want to install a program that's not included in Scoop, it's easy to create an app manifest yourself.
Here's how to create and install a manifest for an 'app' that says hello, in just a few lines of powershell.
# write an app manifest to hello.json
'{ "version": "1.0", "url": "https://gist.github.com/lukesampson/6446238/raw/hello.ps1", "bin": "hello.ps1" }' > hello.json
# install the app
scoop install hello
# did it work?
hello # -> should output 'Hello, <your-username>!'
Share Your Apps
Share On Your Network
If you want others on your network to be able to install from your app manifest, you could just put it on a network share location, e.g. \shared\files\scoop\hello.json. Then, for others to install your app, you can tell them to run:
scoop install \\shared\files\scoop\hello.json
Share With The World
If you make your app manifest publicly available on the web, anyone can install it once they know the URL. For example, I've made a GitHub gist for hello.json here. Now anyone can install it:
scoop install https://gist.github.com/lukesampson/6446567/raw/hello.json
Next steps
If you ran some of these examples, you probably noticed a warning saying 'no hash in manifest'. For reference information on specifying file hashes and much more in your manifests, see the App Manifests reference.
If you want to maintain a collection of apps, see the page on Buckets for more information.
App Manifest Autoupdate
Auto Update is a tool for package maintainers. It automatically checks for new versions of an app and updates the manifest accordingly. It helps to eliminate much of the tedium of updating manifests, as well as reducing the risk of human error while doing so.
Here you will find an in-depth explanation of how the autoupdate
part of an app manifest works.
Using autoupdate
Use checkver
to query the current version of either a specific app or all apps of a bucket.
Open a PowerShell/cmd, then cd
into the buckets repository directory and run the following commands.
To query the current version of a specific app the bucket, run:
cd <bucket repository>
.\bin\checkver.ps1 <app>
To query the current version of all apps of the main bucket, run:
.\bin\checkver.ps1 *
In the output of checkver
, you can see if an outdated app has autoupdate available. If so, you can run the following command to automatically update the respective app's manifest (using *
will update all apps)
.\bin\checkver.ps1 <app> -u
To use a bucket other than the main bucket, specify its directory as the second argument to checkver
.\bin\checkver.ps1 <app> <bucket_dir> -u
It is recommended to verify that the updated manifest still works by installing the app with the following command
scoop install bucket\<app>.json
checkver
to a manifest
Add Simplest solution is to use an regex and it will match it to the source of homepage
. Example: 7zip
homepage
Page where the version can be foundcheckver
Regex for finding the version
"homepage": "http://www.7-zip.org/",
"checkver": "Download 7-zip ([^\\ ]+)",
Use another url if the homepage
doesn't contain the version. Example: gradle
homepage
will be ignoredcheckver.url
Page where the version can be foundcheckver.re
orcheckver.regex
Regex for finding the version
"homepage": "https://gradle.org",
"checkver": {
"url": "https://gradle.org/install",
"re": "The current Gradle release is version ([\\d.]+)"
},
Use a JSON endpoint with rudimentary JSON path expressions to retrieve the version. Example: nuget
checkver.url
JSON endpoint where the version can be foundcheckver.jp
orcheckver.jsonpath
JSON path expression for finding the version (JSONPath Expression Tester)
"checkver": {
"url": "https://dist.nuget.org/index.json",
"jp": "$.artifacts[0].versions[0].version"
},
Use the latest app release on Github by setting checkver
to github
and the homepage
to the repository URL. This will try to match the tag with \/releases\/tag\/(?:v)?([\d.]+)
. The repository maintainer has to use Github's release feature for this to work. Pre-releases will be ignored! Example: nvm
"homepage": "https://github.com/coreybutler/nvm-windows",
"checkver": "github",
Or use different urls for the homepage and repository. Example: cmder
"homepage": "http://cmder.net",
"checkver": {
"github": "https://github.com/cmderdev/cmder"
},
Use capture groups for complex versions and use the results in the autoupdate
property.
This example will provide $version
and $matchShort
as variables. Example: git
"checkver": {
"url": "https://github.com/git-for-windows/git/releases/latest",
"re": "v(?<version>[\\d\\w.]+)/PortableGit-(?<short>[\\d.]+).*\\.exe"
},
checkver
: Regex for finding the version on thehomepage
url
: Page where the version can be found (supports version variables)re|regex
: Regex for finding the versiongithub
: Url to the apps Github repositoryjp|jsonpath
: JSON path expression for finding the version (JSONPath Expression Tester)reverse: true
: match the last occurrence found (default is to match the first occurrence). Example: x264replace
: replace the matched value with a calculated value. Example: sysinternals
autoupdate
to a manifest
Add For the autoupdate feature to work it needs a checkver
property to find the latest version number.
Some example manifests using the autoupdate
feature:
NodeJS,
PHP,
nginx,
imagemagick
"autoupdate": {
"note": "Thanks for using autoupdate, please test your updates!",
"architecture": {
"64bit": {
"url": "https://example.org/dl/example-v$version-x64.msi"
},
"32bit": {
"url": "https://example.org/dl/example-v$version-x86.msi"
}
},
}
"autoupdate": {
"url": "https://example.org/dl/example-$version.zip"
}
All the options can be set globally for all architectures or for each architecture separately
url
: an url template for generating the new url. (supports version variables)- Scoop will rename files by appending
#/dl.7z
or#/pngcrush.exe
to the URL (useful for extracting installers or renaming executables version string)
- Scoop will rename files by appending
extract_dir
: Option to updateextract_dir
(supports version variables)note
: Optional message to be displayed when the autoupdate command is runhash
: Set this property for obtaining hash values without download the actual files.
Some examples with autoupdate and multiple $match
variables:
hash
to autoupdate
Add There are several options to obtain the hash of the new file. If the app provider publishes hash values it is possible to extract these from their website or hashfile. If nothing is defined or something goes wrong while downloading/extracting the hash values the files will be downloaded and hashed locally.
Use the same URL as the file and append .sha256
to it. Example: openjdk
"hash": {
"url": "$url.sha256"
}
Use a different URL to checksums file (can contain multiple hashes and files). Example: nodejs
"hash": {
"url": "https://nodejs.org/dist/v$version/SHASUMS256.txt.asc"
}
Use a different regex to extract the hash. Example: apache
"hash": {
"url": "$url.txt",
"find": "SHA256-Checksum for: (?:$basename):\\s+([a-fA-F0-9]{64})"
}
Use a JSON endpoint with rudimentary JSON path expressions to retrieve the hash. Example: openssl
"hash": {
"mode": "json",
"jp": "$.files.$basename.sha256",
"url": "https://slproweb.com/download/win32_openssl_hashes.json"
}
All the options can be set globally for all architectures or for each architecture separately
mode
:extract
: (default, can be omitted) download fromurl
and find with the regexrdf
: extract from a RDF file. Example: imagemagickjson
: extract from a JSON file. Example: openssldownload
: (fallback) downloads the file and hash it locally
url
: URL template for downloading RDF/JSON files or extracting hashes. It supports the following variables:- Supports all version variables
$url
: autoupdate URL without fragments (#/dl.7z
) [e.g.http://example.com/path/file.exe
]$baseurl
: autoupdate URL without filename and fragments (#/dl.7z
) [e.g.http://example.com/path
]
find|regex
: A regex to extract the hash from the source. [Defaults to:^([a-fA-F0-9]+)$
and([a-fA-F0-9]+)\s+\*?(?:$basename)
]$basename
: filename from autoupdate URL (ignores fragments#/dl.7z
)
jp|jsonpath
: For JSON files: A JSON path to extract the hash from the source (Variables:$basename
)type
: Deprecated, hash type is determined automatically
Version variables
The following variables are available:
$version
:3.7.1
$underscoreVersion
:3_7_1
$dashVersion
:3-7-1
$cleanVersion
:371
- The
$version
(e.g.3.7.1.2
) is splitted on each.
and is assigned to:$majorVersion
:3
$minorVersion
:7
$patchVersion
:1
$buildVersion
:2
$matchHead
: Returns first two or three digits seperated by a dot (e.g.3.7.1-rc.1
=3.7.1
or3.7-rc.1
=3.7
)$matchTail
: Returns the rest of$matchHead
(e.g.3.7.1-rc.1
=-rc.1
or3.7-rc.1
=-rc.1
)$preReleaseVersion
: Everything after the first-
(e.g.3.7.1-rc.1
would result inrc.1
)- Each capturing group in the
checkver
property adds a$matchX
variable (named groups are allowed). Matchingv3.7.1/3.7
withv(?<version>[\d.]+)\/(?<short>[\d.]+)
would result in:$match1
or$matchVersion
:3.7.1
$match2
or$matchShort
:3.7
Limitations
There are some complex manifests which reach the limits of the current autoupdate implementation (The list of affected manifests is incomplete)
- The binaries specified in the
bin
option change with the version number. Example: pngcrush, … - There are multiple
url
needed to be updated. Example: python-exp - The
env_set
is version dependent. Example: sbcl
Testing and running autoupdate
If you want to confirm an autoupdate works (e.g. after adding it to an existing manifest or creating a new one) change the version
field to a lower or different version and then run or use the -f
parameter
cd <bucket repository>
.\bin\checkver.ps1 <app> -u
Check if the url
, extract_dir
and hash
properties have the correct values. Try to install/uninstall the app and submit your changes.
Example Workflow with scoop status/update
scoop status
compares your installed version against the current copy of scoop and bucket repository on your machine. If these are not updated it will output wrong version information. e.g.:
- installed version: 2.1.2
- local scoop version: 2.1.3
- online repo version: 2.1.4
scoop status
will show version 2.1.3
Running scoop update
before scoop status
is recommended (and enforced every 3 hours). Then it will show version 2.1.4
scoop update
just git pull
s the main scoop repo to ~\scoop\apps\scoop\current
and every configured bucket to ~\scoop\buckets\<name>
bin\checkver
is only for maintenance and updating the manifests so they can be committed to the repo.
Example Workflow:
.\bin\checkver -dir ./ * -u
(updates all manifest in the repo)git commit -m "Updated apps"
git push
scoop update
scoop status
scoop update <app>
Persistent Data
Data directory
If you need to store data which should persist between updates you should use ~/scoop/persist/<app>/
.
Inside the manifest, the path to the data directory is available in the $persist_dir
variable.
The PHP package uses it for the configuration files.
App manifest
Directories and files can be added to the persist
definition inside the app manifest.
Persist data is linked from the installed application directory to the data directory with directory conjunctions or hard links.
During the installation, any persistent data is copied into the data directory and linked to.
Definition
The persist
definition can be a string if only one item is needed or an array for multiple items.
Optionally an item can have a different name inside the data directory
{
"persist": [
"keeps_its_name",
["original_name", "new_name_inside_the_data_dir"]
]
}
Examples
Uninstall
There is a flag to purge all persist data when you uninstall an app. By default, the data will be kept until you remove it.
scoop uninstall -p nodejs
Dependencies
Scoop treats dependencies in a way you might not expect, especially if you're used to tools like NuGet or Bundler. These tools have to deal with complex problems around long chains of dependencies for specific versions of libraries. Scoop takes a simpler approach.
Apps are self-contained units.
They should keep their own copies of any libraries they need to run, and not rely on or interfere with any libraries outside their own install path.
It's ok to just install the latest version.
If you were to install Git manually on a new machine, would you go looking for the specific version you had on your last machine to make sure everything works correctly? Or would you just grab the latest version from their website? Scoop assumes you probably want to do the latter.
If this makes you uncomfortable, remember that unlike software libraries which can have breaking changes between minor version increments, this is less common with programs.
Of course there are some cases where an old version of a program is still widely used and the new one isn't backwards compatible, like Python versions 2 and 3. Scoop has these cases covered too: you can install the latest 2.7 version with python27
and for the latest 3.x version it's just python
.
Apps depending on other apps.
Sometimes an app needs another app to install or run properly. For example:
rust
|
|—— innounp required (to install)
| |
| |—— 7zip required (to install)
|
|—— gcc45 required (to run)
|
|—— 7zip required (to install)
In these sort of cases, when you install an app Scoop will assume you want to install anything required that you don't already have and install these automatically too.
Install-time Dependencies vs Runtime Dependencies.
Runtime dependencies are specified with the depends
setting in the app manifest. Install-time dependencies are detected based on the file extensions of the url
s in the app manifest.
Scoop treats these differently, e.g. scoop status
only shows warnings for missing runtime dependencies.
Version dependencies.
For software library dependencies, Scoop just side-steps the problem of version dependencies completely, as described above.
For app dependencies when installing apps, Scoop assumes you want the latest stable version of any dependencies. Again note that Scoop supports specific versions of apps too, e.g. an app could have a dependency on python27
(Python v2.7), or python
(the latest stable version of Python). For updating apps, Scoop just does the bare minimum—if a dependency is completely missing it will install it, but if it's on an older version Scoop will leave it alone.
Version Alias
Current Version Alias
The current
directory for apps is a special alias directory which points to the currently installed version of that app.
It allows path references to remain the same across app updates, since paths refer to the current
directory alias rather than a hardcoded version directory.
For example, if I run ls ~/scoop/apps/git
now, I see this output:
$ ls ~/scoop/apps/git
Directory: C:\Users\luke\scoop\apps\git
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 24/11/16 8:17 am 2.10.2.windows.1
d----- 3/1/17 9:42 am 2.11.0.windows.1
d----l 3/1/17 9:42 am current
The 2.10.2.windows.1
and 2.11.0.windows.1
directories are the installed versions of Git.
The current
directory is not an additional directory, but a Directory Junction, pointing to the 2.11.0.windows.1 directory.
If you're paying close attention, you might notice the extra l
in the Mode
column of the output. But apart from that, you won't see much indication that it's any different from a normal directory.
Directory Junctions
If you're not familiar with directory junctions, you can think of them as similar to symbolic links, or even shortcuts. They are pointers to other locations in the file system. There are some important implementation differences between junctions and symbolic links, which you can read about here if you're interested.
The reason Scoop uses junctions instead of symbolic links is that symbolic links require admin permissions to create (although this looks set to change soon).
Shims Aren't Enough
The main problem being addressed here is how to keep programs working smoothly between updates, even though the new program is in a different directory to the previous one. Shims do solve some of the problems here, by staying in the same place and updating the version that they point to.
However, some programs need to set environment variables, registry settings or other configuration after installation that point to the actual install path. Before Scoop used current
directory junctions, these variables and settings would be pointing to the old directory after an upgrade, which was not ideal. By using a current
alias directory and updating the alias, the settings would continue to point to the right location.