A fast environment resolver for VFX and animation pipelines. YAML package definitions, instant resolution, single static binary. Think Rez, written in Rust, with a small surface area.
cargo install anvil-envOr build from source with cargo build --release and use target/release/anvil.
Write a package at ~/packages/maya-2024.yaml:
name: maya
version: "2024"
requires:
- python-3.11
environment:
MAYA_LOCATION: /usr/autodesk/maya2024
PATH: ${MAYA_LOCATION}/bin:${PATH}
commands:
maya: ${MAYA_LOCATION}/bin/maya
mayapy: ${MAYA_LOCATION}/bin/mayapyPoint anvil at it via ~/.anvil.yaml:
package_paths:
- ~/packagesThen:
anvil list # show available packages
anvil info maya-2024 # inspect one
anvil env maya-2024 # print resolved environment
anvil run maya-2024 -- maya # launch with alias resolution
anvil shell maya-2024 # interactive shellThe commands map lets anvil run <packages> -- <alias> resolve the alias to
its expanded binary path, so you never repeat paths in wrappers or scripts.
A package needs only a name and version. Everything else is optional.
name: houdini
version: "20.5"
description: SideFX Houdini 20.5
requires:
- python-3.11
environment:
HFS: ${PACKAGE_ROOT}
PATH: ${HFS}/bin:${PATH}
PYTHONPATH: ${HFS}/python/lib/python3.11/site-packages:${PYTHONPATH}
commands:
houdini: ${HFS}/bin/houdini
hython: ${HFS}/bin/hython
variants:
- platform: linux
environment:
HFS: /opt/hfs20.5
- platform: macos
environment:
HFS: /Applications/Houdini/Houdini20.5/Frameworks/Houdini.framework/Versions/20.5/ResourcesFlat files live in the package directory as <name>-<version>.yaml:
~/packages/
maya-2024.yaml
arnold-7.2.yaml
Nested directories live as <name>/<version>/package.yaml and are better when
the package ships associated files:
~/packages/
maya/2024/
package.yaml
scripts/
modules/
Both layouts can coexist in one package path.
Used inside requires and at the CLI.
| Form | Meaning |
|---|---|
maya-2024 |
exactly 2024 |
maya-2024+ |
2024 or higher |
maya-2024..2025 |
2024 through 2025 inclusive |
python-3.10|3.11 |
3.10 or 3.11 |
maya |
any version, highest wins |
Names with internal hyphens work (studio-blender-tools-1.0.0); anvil splits
only on the last hyphen when the suffix starts with a digit.
Values resolve in this order: ${PACKAGE_ROOT}, ${VERSION}, ${NAME}, then
any ${VAR} set by previously resolved packages or the inherited environment,
and finally a leading ~/. When two packages set the same variable without
referencing ${VAR} on the right, anvil emits a conflict warning so a silent
overwrite does not slip through.
The commands map lets anvil run pick a program from the package definition.
Values expand the same way as environment values, and can include baked in
arguments with whitespace or tilde segments:
commands:
nukex: ${NUKE_HOME}/Nuke${VERSION} --nukex
usdview: python3.14 ~/USD/bin/usdviewAnvil tokenises the value with POSIX shell rules, expands ~/ in every token,
then runs the first token with the remaining tokens prepended to whatever the
user passes after --.
All twelve commands at a glance.
Print the resolved environment.
anvil env maya-2024 # KEY=VALUE
anvil env maya-2024 --export # shell export lines
anvil env maya-2024 --json # JSON objectRun a command with the resolved environment. The first token after -- is
looked up in the merged commands map.
anvil run maya-2024 -- maya
anvil run maya-2024 arnold-7.2 -- maya -file scene.ma
anvil run maya-2024 -e MAYA_DEBUG=1 -- mayaExits with the command's exit code.
Start an interactive shell with packages loaded. On Unix the shell replaces the current process.
anvil shell maya-2024 arnold-7.2
anvil shell maya-2024 --shell zshanvil list # all package names
anvil list maya # versions of one packageShow one package's metadata, environment, and commands map.
anvil info maya-2024Check that package definitions parse and resolve.
anvil validate # all packages
anvil validate maya-2024 # one packagePin resolved versions to anvil.lock for reproducible environments. Subsequent
anvil env, run, and shell prefer the pinned versions.
anvil lock maya-2024 arnold-7.2
anvil lock maya-2024 arnold-7.2 --updateThe lockfile is YAML. Commit it alongside the project for team wide reproducibility.
Freeze a fully resolved environment to JSON so render farms, CI, or other machines can re-enter it without re-resolving.
anvil context save maya-2024 arnold-7.2 -o render.ctx.json
anvil context show render.ctx.json
anvil context show render.ctx.json --json
anvil context show render.ctx.json --export
anvil context run render.ctx.json -- maya -batch -file scene.ma
anvil context shell render.ctx.jsonScaffold a new package definition.
anvil init my-tools # my-tools/1.0.0/package.yaml
anvil init my-tools --version 2.0 # my-tools/2.0/package.yaml
anvil init my-tools --flat # my-tools-1.0.0.yamlGenerate shell completions. Evaluate the output in your shell rc file.
eval "$(anvil completions bash)"
eval "$(anvil completions zsh)"
anvil completions fish | source
anvil completions powershell | Out-String | Invoke-ExpressionCreate executable wrapper scripts for every command in a set of resolved
packages. The wrappers call anvil run internally, so they always respect
lockfiles, project configs, and command aliases. Drop the output directory on
$PATH and artists call the tools directly.
anvil wrap houdini-20.5 --dir ~/tools/fx
export PATH="$HOME/tools/fx:$PATH"
houdini -scene myfile.hipCopy a validated package to a shared repository. Refuses to overwrite.
anvil publish /studio/packages --path ~/dev/my-tool
anvil publish /studio/packages --path ~/dev/my-tool --flatAnvil reads configuration in this order:
$ANVIL_CONFIGenvironment variable~/.anvil.yaml~/.config/anvil/config.yaml
Anvil also walks the current directory and its parents looking for
.anvil.yaml. When found, it is merged with the global config. package_paths
from the project are prepended, aliases with the same name override globals,
default_shell wins if the project sets it, and per-platform paths are
prepended per-platform.
package_paths:
- ~/packages
- /studio/packages
- ${STUDIO_ROOT}/packages
default_shell: zsh
aliases:
maya-anim:
- maya-2024
- animbot-2.0
- studio-tools
studio-blender:
- blender-4.2
- studio-blender-tools
platform:
linux:
package_paths: [/mnt/shared/packages]
windows:
package_paths: [P:/packages]
macos:
package_paths: [/Volumes/shared/packages]
hooks:
pre_resolve:
- echo "resolving..."
post_resolve:
- /studio/scripts/log_resolution.sh
pre_run:
- /studio/scripts/check_license.sh
post_run:
- /studio/scripts/cleanup.sh
filters:
include: ["maya-*", "arnold-*", "studio-*"]
exclude: ["*-dev", "test-*"]Shell commands run at lifecycle points. A non zero exit from any pre_ hook
aborts the operation; post_run is best effort and never aborts.
| Hook | When |
|---|---|
pre_resolve |
before package resolution |
post_resolve |
after resolution with the resolved env |
pre_run |
before anvil run executes the command |
post_run |
after anvil run finishes |
Limit which packages are visible. Patterns support * and ?.
filters:
include: ["maya-*", "arnold-*"]
exclude: ["*-dev"]| Variable | Purpose |
|---|---|
ANVIL_CONFIG |
override config file location |
ANVIL_PACKAGES |
additional package paths, colon separated |
RUST_LOG |
log verbosity, e.g. RUST_LOG=debug |
If no config file is found, anvil falls back to $ANVIL_PACKAGES,
$HOME/packages, $HOME/.local/share/anvil/packages, and /opt/packages.
Anvil targets the same problem as Rez with a smaller surface area and no Python runtime.
| Anvil | Rez | |
|---|---|---|
| Language | Rust, single static binary | Python |
| Startup | milliseconds | seconds (Python bootstrap) |
| Package format | YAML | package.py |
| Runtime deps | none | Python 3.7+, pip, platform bindings |
| Resolver | greedy | SAT solver with backtracking |
| Install | cargo install anvil-env |
pip install plus rez bind plus config |
| Config surface | one YAML file, three env vars | many files, dozens of settings |
Anvil suits solo TDs and small to medium studios whose packages number in the dozens rather than thousands, where fast iteration and simple operations matter more than advanced constraint solving. Rez keeps the edge on large dependency graphs with complex conflicts, built in build and release tooling, and a centralised package server.
cargo build --release
cargo test
cargo fmt
cargo clippy
RUST_LOG=debug cargo run -- env maya-2024MIT