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:

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:

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:

  1. Clone the nifty repository.
  2. Navigate to the nifty repository local folder.
  3. 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:

  1. Retrieve the specified package definition within the nifty.json file.
  2. Retrieve the specified command within the nifty.json file.
  3. 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:

  1. The package definition for niftylogger is retrieved.
  2. The command configuration for install is retrieved.
  3. 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:

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:

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:

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}}"
}