Package management in Nickel
Nickel's package management is experimental and subject to change! Official
Nickel releases have package management disabled. You either need to download
package-enabled pre-built binaries (ending in -pkg)
or to build Nickel from
source with the --features=package-experimental
flag to enable it.
As part of its mission to reduce your configuration boilerplate, Nickel allows importation of external libraries. These libraries can be imported from
- the local filesystem,
- a git repository (local or remote), or
- the global Nickel package index.
We make a distinction here between importing libraries and importing files.
In Nickel, you can import a local file with the import "path/to/file.ncl"
syntax. Usually, you will use this kind of local import when the file doing the
importing and the file being imported are part of the same project and managed
in the same source code repository. When we talk about importing libraries,
we're talking about importing code that's managed and versioned separately from
the code that's doing the importing. As a consequence, Nickel provides tooling
to help ensure that you're importing an appropriate version of a library, and
obtaining it from the right place.
The manifest file
Nickel's package management requires that you declare up front which libraries
you intend to import. To do this, you write a manifest file, which is just a
Nickel file named Nickel-pkg.ncl
that conforms to the contract
std.package.Manifest
. For example, your manifest file might look like this:
{
name = "my-github-workflow",
version = "1.0.0",
authors = ["Me <me@example.com>"],
minimal_nickel_version = "1.12.0",
dependencies = {
gh = 'Index { package = "github:nickel-lang/github-workflow", version = "1.0.0" }
},
} | std.package.Manifest
This example manifest declares that your local Nickel code depends on the
github:nickel-lang/github-workflow
package from Nickel's global package index,
and that you will be using the shortcut "gh" to refer to that package. Then your
Nickel code can call import gh
, and it will evaluate to the contents of the
github:nickel-lang/github-workflow
package.
When you run nickel eval
or nickel export
, Nickel will look for a manifest
file by starting at the directory containing the file you're evaluating and
searching parent directories until it finds a package named Nickel-pkg.ncl
. So
for example, if your filesystem looks like
.
├── Nickel-pkg.ncl
└── src/
├── ci.ncl
├── release.ncl
└── utils.ncl
and you run nickel export src/ci.ncl
then Nickel will read the Nickel-pkg.ncl
manifest and make available the dependencies that are declared there.
You can change this manifest-finding behavior by using the --manifest-path
option
to specify the manifest location.
Package versions and the lock file
When you specify dependency library in your manifest, you also specify a
version. But the library version that Nickel chooses may not always be
identical to the one you specify. For example, it could be that you ask for
version 1.0 of github:nickel-lang/json-schema
but you also depend on
github:nickel-lang/github-workflow
which depends on version 1.1 of
github:nickel-lang/json-schema
. In this case, Nickel will use version 1.1.
Nickel assumes that all libraries in the global index follow semantic
versioningcargo: versions look like 1.2.3
, in which
1
is the major version, 2
is the minor version and 3
is the patch
version. Libraries indicate a break in backwards compatibility by increasing
the left-most non-zero version, so 2.0.0
is backward-incompatible with 1.2.3
and 0.5.0
is backwards incompatible with 0.4.9
.
When you specify a dependency version in your manifest file, Nickel will pick a
version for you that is at least as large and backwards compatible with the
version you specify. So if you specify 1.2.3
, you could end up with 1.2.5
or
1.3.0
, but not 2.0.0
. The exact chosen version will depend on whether your
other dependencies introduce their own constraints, but Nickel will always try
to find the minimal version that satisfies
everyone.
If you wish to specify the exact version of some dependency, you can do so
using the syntax =1.2.3
. Unlike some other package managers, Nickel does not
support more complex constraints like >= 1.2.3, < 1.3.7
.
Publishing your package
If you have a useful Nickel package, please consider publishing it to the global index! The global index is currently hosted on GitHub; in order to publish a package it will need to be available on GitHub, and you will also need a GitHub account in order to submit a PR to the global index.
The package publishing workflow is currently somewhat involved, but we are working on tooling to improve it:
- Fork the global index (github.com/nickel-lang/nickel-mine) on github.
- Clone your fork onto your local machine.
- The package you want to publish must be in a git repository.
cd
into that repository (or supply--manifest-path
in the next step). - Run
nickel package publish --index <directory-of-your-clone> --package-id github:you/your-package/sub/dir
, assuming that your package lives in the directorysub/dir
relative to the root of your git repository. - You should see that your local machine's index was modified. Commit that modification.
- Push your package to the
you/your-package
repository on github. These names must match the package id in the index (i.e. the argument to--package-id
when you callednickel package publish
), and you must ensure that the version you push to github matches the SHA-1 hash in the index. - Open a pull request to
github.com/nickel-lang/nickel-mine
to make your index modifications public.