src/python | ||
test/integration | ||
tools | ||
.gitignore | ||
BUILD | ||
integration_test.sh | ||
README.md | ||
WORKSPACE |
Cram
To force (people or things) into a place or container that is or appears to be too small to contain them.
An alternative to GNU Stow, more some notion of packages with dependencies and install scripts.
Think an Ansible, Puppet or even NixOS but anyarch and lite enough to check in with your dotfiles.
Overview
Cram operates on a directory of packages called packages.d/
, and two directories of metapackages called profiles.d
and hosts.d
.
Packages
A Cram package consists of a directory containing a pkg.toml
file with the following format -
[cram]
version = 1
[package]
# The package.require list names depended artifacts.
[[package.require]]
name = "packages.d/some-other-package"
# (optional) The package.build list enumerates either
# inline scripts or script files. These are run as a
# package is 'built' before it is installed.
[[package.build]]
run = "some-build-command"
# (optional) Hook script(s) which occur before installation.
[[package.pre_install]]
run = "some-hook"
# (optional) Override installation scrpt(s).
# By default, everthing under the package directory
# (the `pkg.toml` excepted) treated is as a file to be
# installed and stow is emulated using symlinks.
[[package.install]]
run = "some-install-command"
# (optional) Hook script(s) which after installation.
[[package.post_install]]
run = "some-other-hook"
To take a somewhat real example from my own dotfiles -
$ tree -a packages.d/tmux
packages.d/tmux
├── pkg.toml
└── .tmux.conf
This TMUX package provides only my .tmux.conf
file, and a stub pkg.toml
that does nothing.
A fancier setup could use pkg.toml
to install TMUX either as a pre_install
task or by using a separate TMUX package and providing the config in a profile.
Metapackages
Writing lots of packages gets cumbersome quickly, as does managing long lists of explicit dependencies. To try and manage this, Cram provides metapackages - packages which contain no stowable files, but instad contain subpackages.
To take a somewhat real example from my own dotfiles -
$ tree -a -L 1 profiles.d/macos
profiles.d/macos
├── pkg.toml
├── emacs/
├── homebrew/
└── zsh/
The profiles.d/macos
package depends AUTOMATICALLY on the contents of the profiles.d/macos/emacs
, profiles.d/macos/homebrew
and profiles.d/macos/zsh
packages, which are normal packages.
These sub-packages can have normal dependencies on other packages both within and without the profile and install files or run scripts.
Profiles allow users to write groups of related packages, especially configs, which go together and allows for scoped reuse of meaningful names.
Likewise the hosts.d/
tree allows users to store host-specific packages.
Usage
$ cram apply [--dry-run|--execute] [--optimize] [--require <package>] <configdir> <destdir>
The apply
task applies a configuration to a destination directory.
The most common uses of this would be --dry-run
(the default), which functions as a diff
or --execute ~/conf ~/
for emulating Stow and installing dotfiles.
By default cram
installs two packages - profiles.d/default
and hosts.d/$(hostname -s)
.
This default can be overriden by providing --require <package>
one or more times to enumerate specific packages to install.
Cram always reads the .cram.log
state file and diffs the current state against the configured state.
Files and directories no longer defined by the configured state are cleaned up automatically.
$ cram state <configdir>
The state
task loads up and prints the .cram.log
state file generated by any previous cram apply --execute
so you can read a manifest of what cram thinks it did.
This is useful because cram
attempts to optimize repeated executions and implement change detection using the state file.
This cache can be busted if needed by using apply --execute --no-optimize
, which will cause cram to take all actions it deems presently required.
This can result in dangling symlinks in the filesystem.
$ cram list <configdir> [package]
The list
task lists out all available packages (eg. packages, profiles, hosts, and subpackages) as a dependency graph.
When provided a specific package, the details of that package (its requirements and installation task log) will be printed.
License
Copyright Reid 'arrdem' McKenzie, 15/02/2022.
Published under the terms of the Anticapitalist Software License (https://anticapitalist.software).
Unlimited commercial licensing is available at nominal pricing.