Defining tool versions for reliable builds

Defining tool versions for reliable builds

Sep 14, 2023

Multiple versions are installed of tools like Ruby, Node.js and Go on the stacks side-by-side. Since most projects depend on a specific version (implicitly or explicitly), it’s a good idea to declare the required version in workflows instead of relying on the default version available in $PATH.

Installed tool versions are listed in the stack reports ( example)

Bitrise helps selecting the right tool version in three ways:

  1. Common configuration options like the .ruby-version file or the $NODE_VERSION environment variable are automatically recognized during the build. The version declared with these options is automatically activated (if available installed). This is implemented with the asdf project.
  2. Additional versions can be installed at runtime with a single command like asdf install ruby 3.2.0
  3. Some Bitrise steps that depend on a tool - such as the Cocoapods or Fastlane steps - install the declared version automatically (if not available)

How to declare tool versions #

The best way to declare the specific tool version is via version files like .ruby-version, .node-version, .tool-versions, etc. By storing this information in the project repository, every developer’s local environment can match the CI environment by using a version manager (we recommend asdf, which handles the most common tools in one version manager).

The following version files are automatically recognized in Bitrise stacks:

  • .tool-versions ( learn more)
  • .ruby-version
  • .node-version
  • .nvmrc
  • .go-version

By placing this file in the repository root, commands executed in that directory (or its subfolders) will see the declared tool version in $PATH.

Changing a version at runtime is also possible via the asdf CLI tool, for example:

asdf local ruby 3.2
asdf local nodejs 18.16

Version aliases #

Not every possible tool version is preinstalled on the stacks, but there are special alias versions defined for every major and major.minor version. These aliases always point to the latest installed patch version and can be used like full version numbers. For example, declaring Ruby 3.2 selects one preinstalled Ruby version, the latest available patch version of 3.2.x.

Debugging version selection issues #

To see what are the currently selected tool versions and why, run asdf current in a script step:

golang          1.20            /Users/vagrant/.tool-versions
nodejs          18              /Users/vagrant/workspace/.node-version
ruby            3.2             /Users/vagrant/.tool-versions

This shows that Go and Ruby versions are coming from the global .tool-versions file in the user home, while the Node.js version is coming from the current folder’s .node-version file.