So, in Bundler, you can refer to an ASDF .tool-versions file to set the version, with this code in your Gemfile:
ruby file: ".tool-versions"
And with Github CI, the ruby/setup-ruby action will automatically detect the Ruby version from .tool-versions as well (just don’t specify a version manually):
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
… but then on Render, the deploy fails because it decides .tool-versions is actually the version number :
Using Ruby version ".tool-versions" via /opt/render/project/src/Gemfile
...
!!! Failed to download https://raw.githubusercontent.com/postmodern/ruby-versions/master/ruby-".tool-versions"/versions.txt to /opt/render/.cache/ruby-install/ruby-".tool-versions"/versions.txt!
I feel like there’s two things to note here:
.tool-versions is a pretty common way to specify required versions of tools, so it’d be nice if that could be used to detect Ruby (and Node, etc) versions in the build process.
A Gemfile is Ruby code, but Gemfile.lock is not - so it’s much better to parse the version from that as it will be the appropriately calculated value.
I’ve noted this in a new feedback item, as per the discussion in an older thread here, should anyone wish to upvote that. But also, I found the internal Render build script that’s performing this logic (ruby-env.sh) and I’ve made some changes that I think cover off both of these issues, so hopefully this is helpful to accelerate the improvements?
Using the Gemfile.lock/gems.locked to detect the correct Ruby is definitely the way to go! However, the order of precedence is still a problem, unfortunately, the fix might be a breaking one for some.
To give you some context: I’ve kicked off a discussion a while ago in order to pave the way towards a formal specification of the .ruby-version file as part of Rubygems/Bundler. While the discussion is still ongoing, there’s pretty much consensus that .ruby-version is primarily a way to select the Ruby for development, but not for deployment where Gemfile.lock/gems.locked should be used.
This said, it’s quite likely, a formal specification for .ruby-version will allow fuzzy versions which omit the patchlevel (e.g. ruby-3.3 or 3.1) since this is how most Ruby version managers already work and it avoids tedious rebases across feature branches and potentially many developers whenever a new non-breaking patchlevel of Ruby is released.
Render currently reads .ruby-version first and falls back to Gemfile.lock/gems.locked. In other words, it prefers the development setting over the locked Ruby version ready for production. I’re already guessing what comes next.
It should be the other way round: If Gemfile.lock/gems.locked is present, the Ruby version should be read from there since it is locked and controlled by Bundler as are the versions of every Ruby gem in use. Updates of the Ruby version are not “by accident”, but as part of updating the Bundle e.g. with bundle update --ruby. As a fallback, the .ruby-version file should only be read if the project doesn’t use Bundler and therefore using the development Ruby version is the only indication available.
Flipping the precedence order is simple and in general it shouldn’t break projects since the version set locally in .ruby-version makes its way to Gemfile.lock/gems.locked whenever bundle update is used. But there might be edge cases e.g. for projects where Dependabot or similar are creating PR for minor gem updates. So in order to make the change, a heads-up to existing Ruby projects on Render might be a good idea.
Thanks for your input here. We have made the change to prefer taking the Ruby version from the Gemfile.lock over the version listed in .ruby-version / .tool-versions files. This should be live now.
There are also changes coming to our docs. This is just waiting for the PR to be merged.
find: The relative path ‘>>> Updating ruby-".ruby-version" versions ...\n/opt/render/project/rubies/ruby-".ruby-version"/bin’ is included in the PATH environment variable, which is insecure in combination with the -execdir action of find. Please remove that entry from $PATH
Anything I’m missing here?
When we hardcode (i.e. ruby "3.3.0"), it’s just fine to deploy.