Overview
nifty is a simple, self-contained program that can be used as a bare-bones, decentralized (pseudo) package manager and script runner.
It was born out of the necessity of building Nim programs with several dependencies like min or hastysite on machines with low memory (i.e. a VPS running x86 Linux with 500MB of RAM). The main problem was that on such low-end machine it may not even be possible to compile the Nimble package manager, because apparently it requires more RAM to compile than Nim itself.
Nimble offers a lot of features that proper package managers do, like dependency management, package creation and publishing, support for semantic versioning, etc. while nifty does not. Hence nifty is only a _pseudo-_package manager and script runner, but it could be useful in certain situations nonetheless.
Main features
In a nutshell, nifty is a program that executes user-defined scripts on a set of folders or files within a user-define package folder. It doesn’t do (almost) anything by itself, it just relies on other programs and utilities that are typically already available on your system like git and curl to do all the heavy-lifting.
Run side-by-side your existing package manager
nifty doesn’t claim to replace your existing package manager, therefore it tries not to get too much in the way of your existing project structure. All it needs to work resides in a humble nifty.json
file that is used to:
- keep track of what packages are part of the current project
- provide the full definition of all the available commands and how to execute them on specific packages
The folder where packages will be stored is by default set to a packages subfolder within the current project directory, but even this can be configured in the nifty.json
file.
Define your own packages
For nifty, a package can be a folder containing files, or even a single files. Through the nifty.json
file, you can define:
- The source of a package (typically a git repository or event just a URL).
- Whether the package supports git, curl or any other command that will be used to retrieve its contents.
Important
nifty does not support nor understand versioning of any kind. It will not attempt to figure out what version of software you need unless you tell it. This is by design, to keep things simple.
Define your own commands
You can use your nifty.json
to teach nifty new tricks, i.e. how to execute new commands on packages. Your commands look like… well, CLI commands, except that you can use placeholders like {{name}}
and {{src}}
in them for your package name, source, etc.
Run on many different platforms and regardless of the type of project
nifty is a self-contained executable program written in Nim and runs on all platforms where Nim compiles. Also, unlike other package managers that are typically used within the context of one specific programming language (like NPM for Javascript or RubyGems for Ruby), nifty can be used in virtually any project, regardless of the programming language used.
Getting started
Downloading Pre-built Binaries
The easiest way to get nifty is by downloading one of the prebuilt binaries from the Github Releases Page:
Building from Source
You can also build nifty from source, if there is no pre-built binary for your platform.
To do so, after installing the Nim programming language, you can:
- Clone the nifty repository.
- Navigate to the nifty repository local folder.
- Run nim c -d:release nifty.nim
Using nifty
To initialize a new project, run the following command within a folder (it doesn’t have to be empty):
nifty init Project initialized using ‘packages’ as storage directory.
nifty will create a file like the following:
{
"storage": "packages",
"commands": {
"install": {
"_syntax": "install [<package>]",
"_description": "Installs the specified package (or all mapped packages) to the storage directory.",
"git+src": {
"cmd": "git clone {{src}} --depth 1"
},
"git+src+tag": {
"cmd": "git clone --branch {{tag}} {{src}} --depth 1"
},
"curl+src+name": {
"cmd": "curl {{src}} -o {{name}}"
}
},
"upgrade": {
"_syntax": "upgrade [<package>]",
"_description": "Upgrades the specified previously-installed package (or all packages).",
"git+name": {
"cmd": "git pull",
"pwd": "{{name}}"
},
"curl+src+name": {
"cmd": "curl {{src}} -o {{name}}"
}
}
},
"packages": {}
}
Managing packages
After initializing a project, you’d probably want to add some packages your project depends on. First, you must teach nifty where and how to retrieve your packages; this is done by executing the map command, which is used essentially to add or modify a package to the nifty.json
file.
Suppose you want to include “NiftyLogger” in your project. NiftyLogger is a simple logger module written in Nim, and it is constituted by a single file, available within the nifty repository itself:
https://github.com/h3rald/nifty/blob/master/lib/niftylogger.nim
You can create a niftylogger.nim package by running nifty map niftylogger.nim
and specifying some properties that will help nifty manage your package:
nifty map niftylogger Mapping new package: niftylogger (!) Specify properties for package 'niftylogger': -> Name: src -> Value: "https://github.com/h3rald/nifty/blob/master/lib/niftylogger.nim" (!) OK? [y/n]: y (!) Do you want to add/remove more properties? [y/n]: y -> Name: curl -> Value: true (!) OK? [y/n]: y (!) Do you want to add/remove more properties? [y/n]: n Adding package mapping 'niftylogger'... src: "https://github.com/h3rald/nifty/blob/master/lib/niftylogger.nim" curl: true Package mapping 'niftylogger' saved.
The resulting package definition within the nifty.json
file is the following:
You can now:
Managing commands
By default, when you initialize a project the generated nifty.json
file contains two default custom command configurations:
These commands can be used to respectively install and upgrade packages using git or curl. Consider for example the definition of the install command:
Apart for the _syntax and the _description properties that are used internally by the help command, the other properties specify different command-line commands to execute on projects, depending on the properties that have been defined for them.
Executing commands
Considering for example the example of package definition for niftylogger, executing nifty install niftylogger
will effectively execute the following command:
curl https://github.com/h3rald/nifty/blob/master/lib/niftylogger.nim -o niftylogger.nim
Essentially, when a command is executed, nifty will:
- Retrieve the specified package definition within the
nifty.json
file. - Retrieve the specified command within the
nifty.json
file. - find a suitable definition for the specified package. Essentially, nifty will try to look for the command definition related to the specified command that most closely matches the properties defined for the specified package.
In this case, given the example install command and the example niftylogger package:
- The package definition for niftylogger is retrieved.
- The command configuration for install is retrieved.
- The git+src command definition is retrieved, because:
- The definition git+src+tag cannot be used, because niftylogger does not contain a git or a tag property.
- The definition git+src cannot be used, because niftylogger does not contain a git property.
- The definition curl+src+name is used, because niftylogger has a name, a src, and a curl property.
In a similar way, you can modify the commands section of the nifty.json
file and create your own command configurations. Each command configuration can have one or more command definitions identified by the property placeholders used.
Tip
nifty will always try to match the most specific command definition within any given configuration, i.e. the one with the most matching placeholders.
Executing task lists
Task Lists are simple lists of commands to be executed on specific packages. To execute a tasklist, simply specify the name of the tasklist prepended with a $
, like this:
nifty $start-all
In this case, the commands defined in the start-all task list will be executed.
The nifty.json
file format
The nifty.json
file contains information on the current project (for nifty, a project is simply a folder with a nifty.json
in it), organized into three main sections:
- storage
- commands
- packages
The following is an example of nifty.json
file taken from the min repository:
{
"storage": "packages",
"commands": {
"install": {
"git+src": {
"cmd": "git clone {{src}} --depth 1"
},
"git+src+tag": {
"cmd": "git clone --branch {{tag}} {{src}} --depth 1"
},
"curl+src+name": {
"cmd": "curl {{src}} -o {{name}}"
},
"_syntax": "install [<package>]",
"_description": "Installs the specified package (or all mapped packages) to the storage directory."
},
"upgrade": {
"_syntax": "upgrade [<package>]",
"_description": "Upgrades the specified previously-installed package (or all packages).",
"git+name": {
"cmd": "git pull",
"pwd": "{{name}}"
},
"curl+src+name": {
"cmd": "curl {{src}} -o {{name}}"
}
}
},
"packages": {
"nim-sgregex": {
"name": "nim-sgregex",
"src": "https://github.com/h3rald/nim-sgregex.git",
"git": true
},
"nim-miniz": {
"name": "nim-miniz",
"src": "https://github.com/h3rald/nim-miniz.git",
"git": true
},
"nimSHA2": {
"name": "nimSHA2",
"src": "https://github.com/jangko/nimSHA2.git",
"git": true
},
"sha1": {
"name": "sha1",
"src": "https://github.com/onionhammer/sha1.git",
"git": true
},
"nimline": {
"name": "nimline",
"src": "https://github.com/h3rald/nimline.git",
"git": true
},
"niftylogger.nim": {
"name": "niftylogger.nim",
"src": "https://raw.githubusercontent.com/h3rald/nifty/master/lib/niftylogger.nim",
"curl": true
}
}
}
storage
This property is used to specify which directory within the current directory contains (or will contain) packages. By default, it is set to packages.
commands
Custom command configurations are placed within the commands
object. Each command typically contains the following system properties:
- _syntax
- The syntax of the command, displayed when the help command is executed.
- _description
- A brief description of the command, displayed when the help command is executed.
And one or more command definitions, each identified by the placeholders used in it. Command definition contain the following properties:
- cmd
- A command to execute on a specified, containing placeholders for package properties. By default, the name property is always available for all packages, and corresponds to the package identifier.
- pwd (optional)
- The directory where the command will be executed (relative to the storage directory specified in the
nifty.json
file).
Notes
- Command definition identifiers must be composed by plus-separated property names, e.g. git+src, or wget+src+name.
- You can use any of the property names specified in the command definition identifier as placeholders within its definition.
Command configuration example
Consider the following command configuration for the default upgrade custom command:
"upgrade": {
"_syntax": "upgrade [<package>]",
"_description": "Upgrades the specified previously-installed package (or all packages).",
"git+name": {
"cmd": "git pull",
"pwd": "{{name}}"
},
"curl+src+name": {
"cmd": "curl {{src}} -o {{name}}"
}
}
In this case, there are two command definitions:
- git+name
- curl+src+name
packages
Packages definitions are placed in a packages
object. Each package must be identified uniquely by a name, and can contain arbitrary properties that will be used when executing commands.
Typically, you should create properties identifying:
- Where to get the package from, i.e. an URL to a file to download, a git repository, or whatever can be fetched. Typically a property called src is used for this purpose.
- How to get the package, i.e. the name of the actual command to run. Typically, you can just define boolean properties named git, curl, fossil, etc.
Optionally, you can also specify a dir property that will be used by default as the working directory for all the commands executed on the package.
Package definition example
Consider the following definition for a package called nimSHA2:
"nimSHA2": {
"name": "nimSHA2",
"src": "https://github.com/jangko/nimSHA2.git",
"git": true
}
In this case, the package can be fetched from a git repository.
tasklists
Tasklist definitions are optional, but they can be used to execute several commands on specific packages in sequence.
Task list definition example
Considering the following tasklist definition to execute a custom start command on several packages, one after the other:
"start-all": [
"start mydbserver",
"start myapi",
"start myuiserver"
]
Default system commands
The following sections describe the default system commands. Unlike custom commands, system commands cannot be customized and do not require external programs to run.
help
Syntax
nifty help [<command>]
Description
Display help on the specified command (or all tasklists and commands).
info
Syntax
nifty info <package>
Description
Displays information on <package> (essentially all its properties).
init
Syntax
nifty init [<storage-dir>]
Description
Initializes a project in the current directory (using <storage-dir> as storage directory).
list
Syntax
nifty list
Description
Lists all dependencies (recursively) of the current project.
map
Syntax
nifty map <package>
Description
Configures a new or existing package <package>.
The configuration of the package properly is interactive: nifty will prompt whether you want to add or modify properties.
purge
Syntax
nifty purge
Description
Deletes the storage directory and all its contents.
remove
Syntax
nifty remove [<package>]
Description
Physically deletes the specified package (or all packages) from the storage directory.
This command asks for confirmation before deleting each package.
unmap
Syntax
nifty unmap <package>
Description
Unmaps the previously-mapped package <package>, removing all its properties from the nifty.json
file.
update
Syntax
nifty update
Description
Updates the command definitions for the current project and migrate nifty.json file (if necessary).
This program effectively updates your nifty.json
file adding/updating commands and settings, but it does not attempt to remove any custom command configurations or definitions that you may have added.
Default custom commands
install
Syntax
nifty install [<package>]
Description
Installs the specified package (or all mapped packages) to the storage directory, using the matching command definition.
By default, the following definitions are available for this command:
"git+src":
{
"cmd": "git clone {{src}} --depth 1"
},
"git+src+tag":
{
"cmd": "git clone --branch {{tag}} {{src}} --depth 1"
},
"curl+src+name":
{
"cmd": "curl {{src}} -o {{name}}"
}
upgrade
Syntax
nifty upgrade [<package>]
Description
Upgrades the specified previously-installed package (or all packages).
By default, the following definitions are available for this command:
"git+name":
{
"cmd": "git pull",
"pwd": "{{name}}"
},
"curl+src+name":
{
"cmd": "curl {{src}} -o {{name}}"
}