gem-compare: A new way of tracking upstream changes for RubyGems


As a packager and maintainer of various RubyGems for Fedora operating system I am finding myself constantly at upgrading the RPM packages for the new gem releases. This task requires to go through all the changes in the released .gem package and update the SRPM .spec file accordingly. Unlike for the users of the gem I care not only about the new API and runtime dependencies. I have to care about new and deleted files, whether the permissions of those files did change or not, new executables etc.

Since this can take a while and it’s an error-prone process in which is easy to overlook a change, I started to make this process more automatic. The result was a not-so-handsome script that I turned into a RubyGems plugin so it can be used very easily by anyone and be useful to a larger audience.

That means it’s not useful just for Ruby packagers. If you need to know in which release the license went from GPLv2 to MIT or which release introduced an ugly dependency that breaks your code, you can find that very easily with gem-compare. Let’s look on an example:

$ gem compare activesupport 4.0.0 4.1.0 --runtime
Compared versions: ["4.0.0", "4.1.0"]
DIFFERENT runtime dependencies:
4.0.0->4.1.0:
* Deleted:
multi_json ["~> 1.3"] (runtime)
* Added:
json [">= 1.7.7", "~> 1.7"] (runtime)
* Updated:
i18n from: [">= 0.6.4", "~> 0.6"] to: [">= 0.6.9", "~> 0.6"]
tzinfo from: ["~> 0.3.37"] to: ["~> 1.1"]
minitest from: ["~> 4.2"] to: ["~> 5.1"]

The usage is simple. After installation ($ gem install gem-compare) a new gem compare command will be available. The first argument is a gem name followed by versions we would like to see compared. We can also state ‘>4.0.0' or ‘!=2.0' for all the versions that match the expression and ‘_’ for the latest upstream version.

By default all possible comparisons are run and the differences printed to standard output. If we want to narrow the scope of the comparison, we can use additional switches to do so. Notable switches are ‘—runtime’ for comparing runtime dependencies, ‘—platform’ to change to a different platform we are not currently running and ‘—brief’ to include only the most important changes (this is originally intended for packagers and avoids the overwhelming list of changes).

One more option is good to know and understand before starting. The ‘-k’ option will always download .gem files and will keep them in the current directory. This will also enable gem-compare to reuse the already downloaded .gem files and unpacked directories to save traffic and speed things up.

Let’s say I need to know all important changes for prawn since version 1.0.0. The results will look like this:

$ gem compare prawn ‘>=1.0.0' -kb
Fetching: prawn-1.0.0.gem (100%)
Compared versions: [“1.0.0", “1.1.0", “1.2.1"]
DIFFERENT files:
1.0.0->1.1.0:
* Deleted:
manual/manual/
1.1.0->1.2.1:
* Deleted:
lib/prawn/table/
lib/prawn/table/cell/
manual/table/
spec/table/
* Added:
VERSION
DIFFERENT runtime dependencies:
1.0.0->1.1.0:
* Updated:
ttfunk from: [“~> 1.1.1"] to: [“~> 1.2.0"]
pdf-core from: [“~> 0.2.2"] to: [“~> 0.2.5"]
DIFFERENT development dependencies:
1.0.0->1.1.0:
* Added:
prawn-manual_builder [“>= 0.1.1"] (development)
rubocop [“= 0.20.1"] (development)
* Updated:
rspec from: [“>= 0"] to: [“= 2.14.1"]
1.1.0->1.2.1:
* Deleted:
coderay [“~> 1.0.7"] (development)
* Updated:
prawn-manual_builder from: [“>= 0.1.1"] to: [“>= 0.2.0"]

You can see that the brief option now shows only directories that disappeared instead of listing all of the deleted files. Also the ‘version’ and ‘date’ values from the gemspec are no longer compared as they would always differ. But you will still see all of the things you are most likely looking for:

  • gemfile values changes including file lists and dependencies
  • files permissions, executability and shebang changes
  • unexpected permissions for the new files
  • Gemfile dependencies changes

Please check out the project’s GitHub page for more details.

I hope all of this makes tracking upstream changes in time much easier and will be a great addition to a CHANGELOG file. Give it a try and let me know what you think.