Skip to main content

Toolchain

The toolchain is an internal layer for downloading, installing, and managing tools (languages, dependency managers, libraries, and binaries) that are required at runtime. We embrace this approach over relying on these tools "existing" in the current environment, as it ensures the following across any environment or machine:

  • The version and enabled features of a tool are identical.
  • Tools are isolated and unaffected by external sources.
  • Builds are consistent, reproducible, and hopefully deterministic.

Furthermore, this avoids a developer, pipeline, machine, etc, having to pre-install all the necessary tools, and to keep them in sync as time passes.

How it works

The toolchain is built around proto, our stand-alone multi-language version manager. moon will piggyback of proto's toolchain found at ~/.proto and reuse any tools available, or download and install them if they're missing.

Force disabling

The MOON_TOOLCHAIN_FORCE_GLOBALS environment variable can be set to true to force moon to use tool binaries available on PATH, instead of downloading and installing them. This is useful for pre-configured environments, like CI and Docker.

MOON_TOOLCHAIN_FORCE_GLOBALS=true

Additionally, the name of one or many tools can be passed to this variable to only force globals for those tools, and use the toolchain for the remaining tools.

MOON_TOOLCHAIN_FORCE_GLOBALS=node,yarn

Configuration

The tools that are managed by the toolchain are configured through the .moon/toolchain.yml file, but can be overridden in each project with moon.yml.

Version specification

As mentioned above, tools within the toolchain are managed by version for consistency across machines. These versions are configured on a per-tool basis in .moon/toolchain.yml. So what kinds of versions are allowed?

  • Full versions - A full version is a semantic version that is fully specified, such as 1.2.3 or 2.0.0-rc.1. This is the most common way to specify a version, and is preferred to avoid subtle deviations.
  • Partial versions - A partial version is a version that is either missing a patch number, minor number, or both, such as 1.2 or 1. These can also be represented with requirement syntax, such as ^1.2 or ~1. If using partials, we suggest having a major and minor number to reduce the deviation of versions across machines.
  • Aliases - An alias is a human-readable word that maps to a specific version. For example, latest or stable maps to the latest version of a tool, or canary which maps to applicable canary release, or even a completely custom alias like berry. Aliases are language specific, are not managed by moon, and are not suggested for use since they can change at any time (or even daily!).

This sounds great, but how exactly does this work? For full versions and aliases, it's straight forward, as the resolved version is used as-is (assuming it's a legitimate version), and can be found at ~/.proto/tools/<tool>/<version>.

For partial versions, we first check locally installed versions for a match, by scanning ~/.proto/tools/<tool>. For example, if the requested version is 1.2 and we have 1.2.10 installed locally, we'll use that version instead of downloading the latest 1.2.* version. Otherwise, we'll download the latest version that matches the partial version, and install it locally.

Supported tools

The following tools are currently managed by the toolchain.

Bun

  • View the Bun handbook
  • Configured with: bun
  • Installed to: ~/.proto/tools/bun/x.x.x

Deno

  • View the Deno handbook
  • Configured with: deno
  • Installed to: ~/.proto/tools/deno/x.x.x

Node.js

Rust

  • View the Rust handbook
  • Configured with: rust
  • Installed to: ~/.rustup/toolchains/x.x.x