Package management
Any programming language that aims to make collaboration easier needs a way to distribute (and consume) its reusable modules. LIGO provides first-class support for such distributable units (i.e. packages).
Packages
Reusable modules that developers intend to share with others can be
distributed as packages by placing a ligo.json (a manifest file)
next to their Ligo modules.
| ligo.json | set.mligo | list.mligo |
Any directory (recursively) containing .mligo files can be turned into a package
by simply placing a manifest file, ligo.json over there.
LIGO registry
The LIGO registry is used to host LIGO packages. The LIGO registry contains the contracts/libraries along with their metadata. The packages which reside on the LIGO registry can be installed using the ligo install command.
Consuming
To fetch (download) & maintain different versions of external libraries we need a package manager.
LIGO libraries can be published to the LIGO registry as well as npm.
Using ligo install command we can fetch these ligo libraries.
Note:
Earlier versions of LIGO used esy as the backend for package management. This is not so anymore, and installing esy is not necessary.
Workflow
We will need the LIGO compiler to compile smart contracts, to get the LIGO compiler follow these instructions.
Next, we will use a simple dependency @ligo/math-lib published on the LIGO registry. To download & install the library, run,
Now we can write a smart contract which will use the @ligo/mathlib library.
Note: When using LIGO packages via
#import/#includeIf only the name of the package is provided, it will be resolved to the
mainfile of the package.If you want to import a specific file from the package, the syntax is of the form
#import "<pkg name>/<file in package>" "Module"
#include "pkg name>/<file in package>"
and we write some tests for our smart contract in main.test.mligo
To compile the contract to Michelson run the command
This will find the dependencies installed on the local machine, and compile the main.mligo file.
To test the contract using LIGO's testing framework run the command
If you working with an existing LIGO project, to install the dependencies, at the root of the project just run
Specifying package versions
To use package, it is necessary to specify the exact version of the package. Semver ranges are currently not supported. This will, however, change soon.
Upgrading the version of a LIGO package
During the lifecycle of a project, if you wish to upgrade the version of a LIGO package,
Just update the package version to the desired one in the ligo.json. e.g.
and run the command
This will fetch the updated version of the LIGO package, and the compiler will use the updated version of the package.
Using a LIGO package via REPL
If you wish to try out a LIGO package in the REPL environment, Install the LIGO package by following the steps above, and then fire up the LIGO REPL using the following command
Packaging
Packages are code units that can be shared with other developers. Therefore, authors must provide useful metadata, both for other programmers in the community as well as the LIGO toolchain, to understand the package's contents, its version, and other useful information.
Adding package metadata (LIGO manifest)
This is an important step, as it will help the tools and your users/collaborators, provide vital information about your package.
For LIGO packages, authors must provide a manifest file (ligo.json).
The structure of a LIGO manifest is as follows,
Required fields
name: Name of the package.version: Version of the package (Should be a valid sem-ver).main: The main file of the package, Ideally this file should export all the functionality that the package provides.author: Author of the package.license: A valid SPDX license identifier.repository: The place where the LIGO code is hosted (remote repository), Therepositoryfield follows a structure same as npm.bugs: The url to your project's issue tracker and/or the email address to which issues should be reported. Thebugsfields follows a structure same as npm.type: Thetypefield can be one oflibraryorcontract, If the field is ommited default value oftypeislibrarystorage_fn: In the case whentypeiscontract, the name of the function which provides initial storage needs to be provided.storage_arg: In the case whentypeiscontract, an expression that is a parameter to thestorage_fnneeds to be provided.
Optional fields:
description: A brief description of the package.readme: Some readme text, if this field is omitted the contents of README.md or README will be used in its place.dependencies: A object (key-value pairs) of dependencies of the package where key is apackage_nameand the value is apackage_versiondev_dependencies: A object (key-value pairs) of dev_dependencies of the package where key is apackage_nameand value is apackage_version
Sample LIGO manifest (ligo.json) with some of the above information:
Ignore some files or directories while packaging using .ligoignore
You can specify some files or directories which you want to keep out of the LIGO package (keys, deployment scripts, etc.) in a .ligoignore file.
.ligoignore file is similar to a .gitignore file (you can specify glob patterns of files or directories you would like to ignore)
Creating and publishing packages to the LIGO registry
We are going the write a simple package ligo-list-helpers library that is similar to the bigarray package we used earlier.
and some tests for the library
To run the tests run the command
Logging in
Before publishing, the registry server needs to authenticate the user to avoid abuse. To login,
If you're a new user,
This would create a .ligorc in the home directory.
Note: By default, LIGO creates the rc file (
.ligorc) in the home directory.
Publishing
LIGO packages can be published to a central repository at
packages.ligolang.org with the ligo publish command.
Note: while publishing a package If just want to see what LIGO publish would do, you can use the
--dry-runflag$ ligo publish --dry-run
Quick CLI options reference
--cache-path
By default dependencies are installed in the .ligo directory at the root of the project, If you wish to change
the path where dependencies are installed use the --cache-path option to specify the path e.g.
--project-root
LIGO will try to infer the root directory of the project so that it
can find the dependencies installed on your local machine, If you wish
to specify the root directory manually you can do so using the
--project-root option e.g.
--ligorc-path
LIGO creates a .ligorc file to store auth tokens for the user for a specific registry, This auth token is useful when publishing a package.
By default LIGO creates the .ligorc in the home directory, If you wish to override this you can do so using the --ligorc-path e.g.
Note: Using
ligo loginusers can log into multiple registries e.g. LIGO registry, and the LIGO beta registry, A new entry will be created in the.ligorcfor storing auth token of each registry.
--dry-run
While using ligo publish if you don't want to make changes to the LIGO registry, you can use the --dry-run flag. e.g.
This will only display the report on the command line what it would have done in the case of ligo publish.
Unpublishing Packages
Packages can be published in two ways,
- Completely delete the entry
- Only unpublish a specific version
unpublish is that subcommand, grouped under registry subcommand, removes a package or a specific version of it from the registry
Summary
To unpublish a package, run ligo registry unpublish --package-name <name> --package-version <version> from anywhere on the CLI (not necessarily from within a project)
Examples,
If --package-version is skipped, the entire package is unpublished.
Notes
1. Are packages written in different syntaxes interoperable?
Yes, any syntax can be used in packages. Furthermore, one can consume a package written in one syntax from another.
2. What happens if there are entry points declared in a LIGO package?
If you need to use the entry points defined within a package, the best approach is likely to alias them:
In this case, only add entry point from the package will be used by the compiler. By adding an alias for Increment.sub, it is possible to also include that entry point in the final contract.