‘ergo’ is a project dependencies management program and project builder. Given a project stored in a git repository, ‘ergo’ fetches the project and its dependencies (also git repositories), builds them, and loads, tests, or runs the project.
Any git repository, public or private, can be a project or a dependency: there is no central index to register with. Each project selects the versions of its dependencies (branch, tag, release, or commit), and the versions actually used are recorded in a lock file, so that builds are reproducible.
‘ergo’ provides three user interfaces:
$ ergo build $ ergo test
(ergo:load "gitlab:com-informatimago/com-informatimago")
(ergo-load "gitlab:informatimago/some-autolisp-library")
Note: ‘ergo’ is under development; this manual documents the interfaces as designed. Cf. the specifications document for the current status.
A project is a git repository. It may contain an ‘ergo’ project file, ‘<name>.ergo’, listing its dependencies and the commands to build, test, and run it:
(ergo:project
:designator (gitlab "informatimago/clage")
:dependencies ((github "sharplispers/ironclad" :tag :latest)
(github "sharplispers/split-sequence" :tag "v2.0.0")
(gitlab "com-informatimago/com-informatimago"))
:build (asdf "com.informatimago.clage")
:test (asdf "com.informatimago.clage.test")
:run (lisp "(clage:main)"))
For simple projects — a single asdf or alpm system — the project file may be omitted: ‘ergo’ scans the repository, finds the system definition file, and infers the dependencies from it.
A project or a dependency is designated by one of:
A version may be attached to the designator; without one, the repository’s default branch is tracked:
| shell / AutoLISP | Common Lisp | meaning |
|---|---|---|
| ‘…@refactor’ | ‘:branch "refactor"’ | a branch |
| ‘…@v2.0.1’ | ‘:tag "v2.0.1"’ | a tag |
| ‘…@4d16a40…’ | ‘:commit "4d16a40…"’ | a commit hash |
| ‘…@latest’ | ‘:tag :latest’ or ‘:release :latest’ | latest tag or release |
A collection is a named site serving git repositories: gitlab.com, github.com, a private gitlab or gitea instance, or a plain git server. The collection database is made of files of ‘ergo:define-collection’ forms, in three layers: built-in (distributed with ‘ergo’), site (the ‘ergo/collections.d/’ subdirectory of the ‘$XDG_CONFIG_DIRS’ directories), and user (‘$XDG_CONFIG_HOME/ergo/collections.d/’, ie. ‘~/.config/ergo/collections.d/’ by default). A user definition overrides a site or built-in definition with the same name.
;; ~/.config/ergo/collections.d/my-collections.lisp (ergo:define-collection origin :kind plain :git-user "pjb" :git-server "git.informatimago.com" :git-root "/srv/git/public")
‘ergo collections’ lists the database in search order.
A collection maps a repository path to a url; the system-name index maps a bare system name (‘alexandria’, ‘hunchentoot’) to a repository designator, so a dependency that ships no ‘.ergo’ file can still be found by name. It is made of ‘ergo:index-system’ forms, in the same three layers as the collection database (built-in, site, user); a higher layer overrides a lower one.
;; ~/.config/ergo/index.d/user.lisp (ergo:index-system "my-lib" (git "https://example.org/me/my-lib.git"))
‘ergo index add <name> <designator>’ writes a user entry; ‘ergo index <name>’ shows the matches; ‘ergo search’ consults the index too.
The built-in layer is generated from the quicklisp-projects data (the same catalogue quicklisp uses), so ergo resolves the ~2300 git-hosted quicklisp libraries by name. Installing ‘ergo’ generates a fresh index into its built-in layer (‘make install’ runs the generator; opt out with ‘GENERATE_INDEX=0’, eg. for an offline build).
Quicklisp-projects is updated roughly monthly. Refresh your copy whenever you like — the way you update a quicklisp dist when you choose — by running the installed generator over a fresh checkout, writing to your user layer so it overrides the system one:
$ git clone --depth 1 https://github.com/quicklisp/quicklisp-projects.git
$ sbcl --script "$PREFIX/share/ergo/generate-quicklisp-index.lisp" \
quicklisp-projects \
~/.config/ergo/index.d/quicklisp.lisp \
~/.config/ergo/index.d/quicklisp-skipped.txt
Sources quicklisp fetches by other means — tarballs, plain ‘http’ files, ‘svn’, ‘darcs’, ‘mercurial’ — are not git, so ergo cannot fetch them as-is; they are listed in the generator’s skip report, and you can give any of them a git mirror with an ‘ergo:index-system’ entry of your own, which — being in a higher layer — wins over the generated one.
The project file expresses intent, and may be loose: tracked branches, ‘latest’ tags. When ‘ergo’ resolves it, it writes the lock file, ‘<name>.erglock’, next to the project file: the complete dependency graph, with the exact commit of each dependency, direct or transitive.
Commit the lock file with your project. All the build, test, run, and load operations obey it exactly: everyone rebuilding the project gets the very same sources. The lock file changes only when you run ‘ergo update’ — review the diff, and commit it.
Clones are kept in a shared cache:
~/ergo/repositories/{collection}/{path}/{branch-or-tag-or-commit}/
Two projects requiring the same version of a dependency share the clone; requiring different versions, they use separate directories. Repositories with the same path in different collections (eg. ‘gitlab:foo/bar’ and ‘github:foo/bar’) are distinct and can coexist.
ergo <subcommand> [option…] [argument…]
The command determines the current project by searching the current directory, then its ancestors, for a ‘*.ergo’ project file. Subcommands taking an optional designator default to the current project.
change to ‘<dir>’ before doing anything.
use ‘<file>’ as the project file.
use only the lock file and the local cache; fail rather than access the network.
report what would be done, without doing it.
resolve collection-qualified designators (‘github:…’, ‘gitlab:…’, …) to their ‘https’ url. This is the default: an anonymous, read-only clone that needs no ssh key, which is what you want for fetching and in CI.
resolve them to their ‘git@…’ (ssh) url instead, for push access — use it to clone a dependency you intend to edit and push back. A specific designator (‘(git "…")’) always keeps its own url; only the url synthesised from a collection changes. Because a repository’s identity is transport-independent, ‘--ssh’ and ‘--https’ share one cache entry and one lock entry: switching neither re-clones nor perturbs the lock. (An already-cached mirror keeps the transport it was first cloned with; remove it, or use a fresh cache, to re-clone over ssh.) The default can be set with ‘(ergo:configure :git-transport ssh)’.
show the underlying git and build commands.
print the results as s-expressions, for tools.
print the ‘ergo’ version and exit.
print the usage and exit; after a subcommand (‘ergo build --help’), print the usage of that subcommand.
The version options ‘--branch <name>’, ‘--tag <name>’, ‘--release <name>’, and ‘--commit <hash>’ may be used instead of the ‘@<ref>’ suffix when the ref kind must be explicit.
clone or pull the project and its dependencies, obeying the lock file; no build.
fetch, then build the project and its dependencies.
fetch, build, then run the tests.
fetch, build, then run the project; the arguments after ‘--’ are passed to the program.
fetch, build, test, and if possible run.
start the interactive environment of the project with the project loaded: a Common Lisp REPL for asdf projects, an AutoLISP REPL for alpm projects, a shell in the project directory otherwise.
re-resolve the loose designators of the project file — all of them, or only the given components — and rewrite the lock file.
search the collections for repositories matching ‘name’; ‘--collections <c1>,<c2>,…’ restricts the search.
report the provenance: url, branch, tag, or commit.
print the directory of the local clone.
drop one repository’s clone from the cache — its bare mirror and every per-ref worktree. Unlike ‘ergo gc’, which sweeps worktrees no lock still references, this removes one repository outright.
restrict a search to the repositories already materialized in the local cache.
report the current project, the freshness of the lock file, and any local modification in the clones.
create an ‘ergo’ project file in the current directory, inferring the class of project and the dependencies; with a designator, fetch that project first.
list the collection database, in search order, with the layer providing each collection.
remove the build products of the project.
Subcommands exit with status 0 on success, and non-zero on failure; diagnostics are printed on the standard error stream.
$ ergo search split-sequence github:sharplispers/split-sequence https://github.com/sharplispers/split-sequence.git $ ergo init gitlab:informatimago/clage $ ergo build $ ergo test $ ergo update cl-babel/babel $ git diff clage.erglock $ git commit -am 'Updated babel.' $ ergo run -- --help
The ‘ERGO’ package provides the same operations inside a Common Lisp image; the loading operations load the systems into the current image, like ‘ql:quickload’.
Designators are given either as strings, in the same syntax as the command line (‘"gitlab:owner/repo@v1.2"’), or as forms: ‘(gitlab "owner/repo" :tag "v1.2")’, ‘(git "https://…/repo.git" :branch "dev")’.
fetch, build, and load the project into the current image.
fetch and build (compile), without loading.
fetch, build, and run the tests.
fetch, build, load, and run the project, as specified by its ‘:run’ clause.
fetch, build, load, test, and if possible run.
re-resolve the loose designators and rewrite the lock file.
re-read the project from its local clone and force-reload it into the image, offline — pick up local edits or a prior ‘ergo:update’ in a dev loop, the way quicklisp’s ‘clear-system’ + reload does.
like ‘ergo:load’, but return the sorted names of the packages the load newly defined — a quick way to discover what a system provides.
remove one repository’s clone from the cache; return the removed directory.
search the collections; return the list of matches.
return the provenance (url, branch/tag/commit) of the project or component.
the macro used in project files; cf. the Project File section above.
the macro used in the collection database files; cf. the Collections section above.
(ergo:load "gitlab:com-informatimago/com-informatimago") ;; clones (or pulls) the repository and its dependencies, ;; compiles, and loads the systems into the image. (ergo:test "github:sharplispers/split-sequence@v2.0.1") (ergo:where-from "com-informatimago/com-informatimago") ;; → url, branch, and commit of the loaded version.
The ‘ergo’ AutoLISP package provides the same operations inside an AutoLISP engine: AutoCAD, BricsCAD, or clautolisp. It requires:
AutoLISP has no packages: the functions are prefixed with ‘ergo-’. Designators are given as strings, in the command line syntax.
fetch, build, and load the alpm system into the engine.
force-reload the alpm system into the engine (pick up local edits or a prior ‘ergo-update’ without restarting).
fetch and build, without loading.
fetch, build, and run the tests.
fetch, build, load, and run.
re-resolve and rewrite the lock file.
remove the repository’s clone from the local cache.
search the collections; return the list of matches.
return the provenance.
(load "ergo.lsp") (ergo-load "gitlab:informatimago/some-autolisp-library@v1.2") ;; the library and its dependencies are fetched and built by the ;; ergo command, then loaded into the engine with alpm.
the project file: designator, dependencies, resolutions, build/test/run clauses. Written by the author (or generated by ‘ergo init’).
the lock file: the resolved dependency graph with exact commits. Generated by ‘ergo’; committed with the project; rewritten only by ‘ergo update’.
the user configuration.
the user layer of the collection database.
the site layer of the collection database.
the shared cache of cloned repositories, indexed by collection, path, and version.