As announced in my previous post I will document my journey of learning Ruby
and especially Ruby on Rails. Or how a friend of mine put it after hearing
about my idea:
Blogging about Rails like it's 2009 😄.
@dbrgn February, 2022
Learning Ruby like it's 2009
One of my first exercises was to investigate a case where an automatic update
done by the depfu-bot failed the tests and as such
couldn't be merged. It looked similar to
this but failed the tests and
had a lot more updates including some major ones.
Already knowing a few programming languages and package managers my first idea
was: «Easy: Let's just first do all the minor and patch upgrades and see if
this breaks anything». Assuming the gems follow semantic versioning this should
just work.
Bundler and version specifier
First thing I noticed is that Bundler, while recommending semantic versioning,
doesn't directly support semantic version specifiers like for example
cargo does. It does
support defining your own ranges like the following:
gem 'foo', '>= 2.2.0', '< 3.0'`
or using the ~>
operator which achives the same thing:
For
cargo
one would just specify
with the same result and in
npm we can use the ^
operator:
For bundler there is an issue on
GitHub to add a SemVer
operator since 2017.
The code base I was working on didn't have a lot of version specifiers. The
reason is that Renuo generally tries to use the latest versions of all Ruby
dependencies for applications. This is possible since we have extensive test
suites which we trust and it enables an open and fast upgrade path for
customers if they need new features in the future.
Exceptions are made if a gem update causes problems or if it is a very central
gem like rails. To still have reproducible environments for applications the
Gemfile.lock
file is of course checked into source control as it is best
practice.
So just running bundle update
would update to the latest major version of
almost all dependencies, which was exactly the thing I didn't want to do.
Luckily bundler has the
--minor
option which one
can pass to the update
command which should Prefer updating only to next
minor version.
A minor inconvenience
So here we go executing bundler update --minor
! And the result was...
Nothing! It just didn't update anything!
Confused I started reading up documentation, version specifiers, tried to
update individual packages with bundle update --minor $package
(which worked)
and finally found https://github.com/rubygems/rubygems/issues/3360. Apparently
it's a known issue since 2018 that bundle update just fails to do the --patch
and --minor
updates.
From the same GitHub issue I copied the handy shell one-liner bundle update
$(bundle list | awk '$1 ~ /^\*/ {print $2}' | grep -v bundler) --minor
which
did the job!
Doing just the semver compatible updates worked, all the tests did pass and we
could deploy it. My shattered trust in the Ruby ecosystem from the bunlder
failure was restored!
All in all bundler is a decent package manager and I love that the Ruby
ecosystem seems to embrace semantic versioning and one can do fearless upgrades
to get bug- and security fixes of the dependencies.