public
Last active

A Common .ruby-version File For Ruby Projects

  • Download Gist
README.md
Markdown

A Common .ruby-version File For Ruby Projects

Background

I've been using this technique in most of my Ruby projects lately where Ruby versions are required:

  • Create .rbenv-version containing the target Ruby using a definition name defined in ruby-build (example below). These strings are a proper subset of RVM Ruby string names so far...
  • Create .rvmrc (with rvm --create --rvmrc "1.9.3@myapp") and edit the environment_id= line to fetch the Ruby version from .rbenv-version (example below).

Today I learned about another Ruby manager, rbfu, where the author is using a similar technique with .rbfu-version.

So...

What if we had an ecosystem of fabulous Ruby managers that all understood the semantics of a generic dotfile such as .ruby-version? The file's contents would be nothing more than a string representing a version of Ruby.

Perhaps We Can

Without a more thorough investigation (here be dragons?), the project-level updates might be:

  • rvm: A modification to scripts/functions/rvmrc to check for .rvmrc and then .ruby-version (invoking something like rvm use $(cat $working_dir/.ruby-version)). If the user requires a customized .rvmrc they can wire in .ruby-version themselves (i.e. environment_id="$(cat .ruby-version)@gemset").
  • rbenv: A modification to libexec/rbenv-version-file to check for .rbenv-version and then .ruby-version.
  • rbfu: A modifcation to bin/rbfu to first check for .rbfu-version and then .ruby-version.

In all 3 cases, it seems reasonable to prefer an implementation-specific file over the generic version--no loss of default behavior.

So?

Feedback? Ideas? Questions?

Sample.rbenv-version
1
1.9.3-p125
Sample.rvmrc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
#!/usr/bin/env bash
 
# This is an RVM Project .rvmrc file, used to automatically load the ruby
# development environment upon cd'ing into the directory
 
# First we specify our desired <ruby>[@<gemset>], the @gemset name is optional,
# Only full ruby name is supported here, for short names use:
# echo "rvm use 1.9.3" > .rvmrc
environment_id="$(cat .rbenv-version)@myapp"
 
# Uncomment the following lines if you want to verify rvm version per project
# rvmrc_rvm_version="1.10.3" # 1.10.1 seams as a safe start
# eval "$(echo ${rvm_version}.${rvmrc_rvm_version} | awk -F. '{print "[[ "$1*65536+$2*256+$3" -ge "$4*65536+$5*256+$6" ]]"}' )" || {
# echo "This .rvmrc file requires at least RVM ${rvmrc_rvm_version}, aborting loading."
# return 1
# }
 
# First we attempt to load the desired environment directly from the environment
# file. This is very fast and efficient compared to running through the entire
# CLI and selector. If you want feedback on which environment was used then
# insert the word 'use' after --create as this triggers verbose mode.
if [[ -d "${rvm_path:-$HOME/.rvm}/environments"
&& -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
then
\. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
[[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]] &&
\. "${rvm_path:-$HOME/.rvm}/hooks/after_use" || true
else
# If the environment file has not yet been created, use the RVM CLI to select.
rvm --create "$environment_id" || {
echo "Failed to create RVM environment '${environment_id}'."
return 1
}
fi

that's very nice, would it be possible to make it more repeatable process ? there are things like ruby configuration/flags that might be project specific.

how about:

ruby=ruby-1.9.3-p125
patches=falcon,debug

or for jruby:

ruby=jruby-1.6.7
JRUBY_OPTS=--1.9

the main idea is to keep ruby name in ruby=... and allow additional params, I do not hide that would be also useful for naming gemset for rvm.

We could make a list of standard options - that are most useful.

and here is how would you read ruby version of it:

sed -n '/ruby=/ {s/ruby=//;p;}' < .ruby-version

although other ways, including sourcing could work too

Well, there you go:

https://github.com/hmans/rbfu/commit/7462b4f30c5a851ea45d2171671adb48e9cc35f9

I believe the format of the file should be kept as simple as possible -- ie, a Ruby version, nothing else -- otherwise there's going to be too much fragmentation across the various different tools.

@hmans Sweet! About as tough as I hoped it'd be.

@mpapis I agree that sometimes additional configuration is nice (even required), but I hope that we could hit the 80 of 80/20 for most people's use cases.

my only concern is that to simple format makes it very limited, it can not be extended because of it's simplicity, this makes it required to duplicate files to provide customization.

what issues do you see in a format like this:

ruby=jruby-1.6.7
env.JRUBY_OPTS=--1.9

it's very simple to parse and provides a lot more flexibility, also could provide additional details related to project ... it's extensible

I see this as possibility to improve the world by including extensions like:

ruby-gem-install=bundler,rake
ruby-bundle-install=true
run-script=scripts/setup-project.sh

after thinking we are close to allow something more, especially with projects like https://github.com/kwatch/versionswitcher:

ruby=1.9.3
node=0.4.7

just need to get with a good file name, and use ruby= for ruby switchers, rest is our imagination

.required.conf
.versions.conf
.vers.conf
.ver.conf
.reqvers.conf

All my suggestions for names.

https://github.com/wayneeseguin/rvm/commit/60bdc82ef4c9ab879e12192133f8808e81f37149 :

  • add support for .ruby-version / .rbfu-version / .rbenv-version, can be generated with --ruby-version, will save gemset name to the file ...
  • add support for .versions.conf, can be generated with --versions-conf

example config:

ruby=ruby-1.9.3-p125
ruby-gemset=test
#ruby-gem-install=bundler rake
#ruby-bundle-install=true

it will install gems listed in ruby-gem-install=... separated with spaces/comas
will run bundle install if ruby-bundle-install=true or bundle install Gemfile.x if ruby-bundle-install=Gemfile.x

@mpapis So if I read scripts/functions/rvmrc correctly, this implements the simplest case of including a bare Ruby string in .ruby-version (among others)? This is great!

yes it does, the only missing thing is support for sanitizing env. variables so I could read in env.*=... from .versions.conf and .rbenv-vars

Guys, have you seen autoenv? Consider how this may factor into this discussion. Most teams working on a project will have their toolchain pretty much locked down and may be better off using autoenv to define how the environment is set up for their project.

I'm happy to support .ruby-version in rbfu, but I think anything beyond just mentioning a Ruby version in that file would expand the scope so much that using something like autoenv may be a better solution altogether.

My 2 Eurocents... :)

Speaking of which, I'm seriously considering removing the "automatic mode" from rbfu and just referring people to autoenv instead. This would go along nicely with rbfu's design goal to pretty much do as little as possible. It will still happily read .rbfu-version and .ruby-version files, of course; just not automatically after cd'ing into a new directory.

@hmans I helped in implementing parts of autoenv, and it has few issues:

  1. it requires authorization of the file like .rvmrc - which is one of the confusing points and why .rbenv-version was created in first place
  2. it is just an shell script not standardizing anything at all you would need to put there case statements to detect which manager is available.
  3. the tests are not maintained and not connected to travis-ci.org - so you can not expect it will be always functional as it should be https://github.com/kennethreitz/autoenv/issues/13

I just implemented the comment support for Gemfiles, so when there is no other format rvm will try to read:

#ruby=1.9.3
#ruby-gemset=my-project

from Gemfile and if you have in ~/.rvmrc:

rvm_autoinstall_bundler_flag=1

rvm will also install bundler if not available and always run bundle install

it's very fast with the new bundler-1.1

It starts to be fun: https://github.com/wayneeseguin/rvm/commit/577e53c888f6e0bfc4d77b0a15d5168a27334c4f

Quick update regarding rbfu: I've hardened the support for .ruby-version, added some documentation about it (in fact, suggesting people use .ruby-version instead of .rbfu-version), and will blog about the effort to get this added to RVM and rbenv once I release 0.3.0 (which may be some time this weekend.)

I've sort of lost track, has anyone actually submitted a pull request for this to rbenv yet?

Regarding documenting the Ruby version on Gemfile, I'm sorry to say that I'm not a big fan. Gemfile and Bundler don't need to know about Ruby versions, and I'm pretty sure that the maintainers of Bundler aren't going to make this yet another Ruby version manager, so I'm passing on this idea for now.

@hmans That's great news indeed! There is an open issue and a pull request for rbenv but I'd bet that the dev team has been slammed getting http://basecamp.com out the door this week. I'm sure we'll hear from Sam and gang once they're back into GitHub issue triage.

@hmans so yo uare right and there is no place for ruby-version in Gemfile / bundler

but I was talking with @evanphx and he liked this proposal and it most likely will make it's way to the new format replacement of Gemfile in rubygems 2

my opinion on this is that we can fit the information for the project in one place - so if the new format supports custom fields you can add more information - whatever you need to make it working

as for RVM we have release planned around end of the month and I will have documentation updated for it in the last week of March

Could someone explain to me why you would want to nail a project down to a single install of ruby anyway?

When I work on projects I use the default ruby I setup in my home config and that's it. CI testing checks the other versions.

@trans I agree with you most of the time, although I make an exception for Rails/Sinatra applications. Unless it's an OSS app (like GitLab, Errbit, etc.), I'm most likely targeting a Ruby version in production. If nothing else it's an additional piece of project documentation. Otherwise, yeah I love to target all the rubies!

FWIW, I've just tagged and released rbfu 0.3.0, probably the last 0.x version before I tag a 1.0.0 and have another go at courting the Homebrew guys to include the rbfu formula.

chruby 0.3.0 will support optional auto-switching, which will use the .ruby-version file. However, chruby will allow .ruby-version to contain sub-strings of the fully qualified Ruby name (ex: jruby or ruby-1.9).

@postmodern nice!

chruby 0.3.0 has been released, so chruby now supports .ruby-version as well. :cake:

@fnichol can we add a small section on the contents of .ruby-version. I think .ruby-version may contain a sub-string that is fuzzy-matched against available Rubies. This would allow developers to specify jruby or ruby-1.9, and avoid specifying the exact version.

rbenv is going to support .ruby-version, but definitely without any fuzzy matching. Here's why I think fuzzy matching is a bad idea:

  1. Let's suppose I have 1.9.3-p0 installed.
  2. I put "ruby-1.9" to .ruby-version in my project and all is well.
  3. After some time I install 1.9.3-p300 to try it out and suddenly all my projects marked with "ruby-1.9" automatically upgrade to it. Gems need to be reinstalled, native extensions need to be upgraded, etc. Nightmare.

One of the important reasons why we have version managers is to be precise about versions. rbenv is going to be precise. If you want cute shortcuts & aliases, you can always make symlinks.

@postmodern Cheers on getting 0.3.0 out the door, one more on board!

@mislav I can get behind the reasoning for this here and I largely agree with you for the same reasons I would commit a Gemfile.lock to a web application (vs. a gem library).

It looks like the active branch for .ruby-version in rbenv is being tracked in https://github.com/sstephenson/rbenv/pull/302, looks pretty solid and complete to me.

I'd love to see Heroku adopt this emerging standard.

My approach is to have a per project rc file. I call mine .prvmrc and it is simply sourced e.g. . .prvmrc (kept at the top directory of the project)

This way it can have any shell commands you want. Normally it sets PATH, GEM_HOME, and GEM_PATH but it can also set the postgresql database default, etc. It does not set PATH directly but rather calls some shell functions. So, the weight is a few rather small shell functions added to the shell's profile.

I also have my bundler put the gems and bin in the .bundle subdir of the project so the front of the path becomes the path to the directory plus .bundle/bin. e.g. PATH=/path/to/app/.bundle/bin:/usr/local/bin:/usr/bin:...

There are no shims involved. .bundle/bin is seeded with symlinks to the original commands and the symlinks are over written as the gems (e.g. rake) are updated into the local project.

Is there a convention for comments in a .ruby-version file?

@mpapis Does RVM currently support reading .ruby-version?

I realized you have a commit for this issue https://github.com/wayneeseguin/rvm/issues/786

But I just could not find the docs from rvm.io

@leomao10 just tested w/ rvm 1.18.20 and it works

Just a note. Seems like RVM 1.18.21 now generates two files when rvm --create --ruby-version use ruby-1.9.3-p392@my_project is run. The files are .ruby-version and .ruby-gemset.

fry (fish ruby switcher) is also supporting this if you enable auto-switching, it comes with fuzzy matching and ruby- prefixed is matched against non-ruby prefixed rubies.

# Fuzzy
ruby-1.9 => 1.9.3-p392
1.9 => 1.9.3-p392
jruby-1.7 => jruby-1.7.3

# Non-fuzzy
ruby-1.9.3-p392 => 1.9.3-p392
1.9.3-p392 => 1.9.3-p392
jruby-1.7.3 => jruby-1.7.3

@terlar nice to see another Ruby switcher supporting fuzzy matching. Specifying the fully qualified version is over-kill in most cases.

Guys, :+1 now only - don't complicated any more, and I certainly don't like the idea of just saying "jruby", will break on updates for granted. ;)

RVM (head/1.22.0) now supports also .ruby-env for setting environment variables when entering project directory, more details here: https://rvm.io/workflow/projects

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.