asdf: plugin add and install from .tool-versions
asdf needs a plugin per language: asdf plugin add <name>, then asdf install reads .tool-versions and installs every pinned tool at once.
asdf manages many runtimes (Node, Python, Ruby, Go, Terraform, and more) through plugins, with one .tool-versions file pinning all of them. The two-step plugin-then-install model is what trips up new CI setups.
What it does
asdf is a plugin-based version manager. Each language is a plugin you add once; asdf install then reads .tool-versions (lines like nodejs 20.11.1) and installs every listed version. Shims under $ASDF_DATA_DIR/shims dispatch commands to the pinned version.
Common usage
asdf plugin add nodejs
asdf plugin add python
asdf plugin add terraform
# .tool-versions:
# nodejs 20.11.1
# python 3.12.3
# terraform 1.8.2
asdf install # installs everything pinned
node -v && python --version && terraform versionOptions
| Command / flag | What it does |
|---|---|
| asdf plugin add <name> | Register a language plugin |
| asdf install | Install all versions in .tool-versions |
| asdf install <name> <v> | Install one specific version |
| asdf local <name> <v> | Pin a version in .tool-versions |
| asdf global <name> <v> | Pin a version in ~/.tool-versions |
| asdf reshim | Regenerate shims after installing tool binaries |
In CI
Run asdf plugin add for each tool before asdf install, since a fresh runner has no plugins. After installing a tool that ships extra binaries (e.g. a global npm package), run asdf reshim. Cache $ASDF_DATA_DIR (installs and plugins). Note asdf 0.16+ rewrote the tool in Go; older versions sourced asdf.sh instead.
Common errors in CI
"No preset version installed for command node" / "No version is set for command" means .tool-versions is missing the entry or asdf install was not run. "Unknown command: 'install nodejs'" can mean the plugin was never added. "asdf: command not found" on legacy versions means asdf.sh was not sourced. After a global tool install, "command not found" is fixed by asdf reshim.