We recently had a really old app roll into our shop: a Rails 4.1 app, running Ruby 2.3. The git history was a bit convoluted, but the app is definitely no younger than 10 years old. How do we get it running on a Mac with Apple silicon? Dev Containers to the rescue!
After cloning the repo locally, we open it up in VS Code. We add a new hidden folder, .devcontainer
to the root of the project, and then add four files:
The Dockerfile
Our Dockerfile
will start with the official ruby:2.3.4 image
, which uses Debian Jessie.
Jessie is end-of-life, so apt-get won't work and you won't be able to use it to install packages. Fortunately, there is a work-around. We can manually update the OS's package source list to pull from the archives. We can also disable key checking, as the signature keys will most definitely be expired:
Next, our app also relied on the TinyTDS gem, which needs the FreeTDS package. These lines will download and build it for us:
The Docker Compose File
Our docker-compose.yml
file is pretty standard. We're using the latest PostgreSQL 9.6 image - later versions have security features that don't play well with this old app.
Also worth noting: this will create a new volume, postgres-data
, to persist the database data. It will also copy a database initialization SQL command (.devcontainer/create-db-user.sql
)
Here's what we put in the .devcontainer/create-db-user.sql
file. Note that dev containers, by default, are run as a user called vscode
.
The Dev Container JSON File
Finally, the devcontainer.json
file includes some basic dev container configurations. Note that we're using the postCreateCommand
section to install the gems (bundle install
) and to initiate the database.
App Tweaks
Naturally, there will be some small app tweaks necessary to get the app running properly, but as a general goal, we tried to make as little changes as we could to the app to get it to run. Here's what we found:
Internalizing Unsupported Gems
We found two very niche and abandoned gems that the app relied on. In the interest of maintainability, we internalized the gems by copying them into the /vendor/gems
folder.
Where our Gemfile
used to have this:
gem 'weird_gem', :git => 'https://github.com/author/weird_gem.git', :branch => 'weird-branch'
we now just refer to the local copy of the gem:
gem 'weird_gem', path: 'vendor/gems/weird_gem'
Add Long-Lost Dependency Gems
This app uses the paperclip
gem, which has long since been abandoned, but at least still exists on Github. However, one of its dependencies, mimemagic
, specifically version 0.3.0
is no longer available as a release. We had to dig through that gem's commits to find the commit at that specific version, and refer to that in our Gemfile, which now looks like this:
gem 'mimemagic', git: 'https://github.com/mimemagicrb/mimemagic', ref: 'a4b038c6c1b9d76dac33d5711d28aaa9b4c42c66'
What Version of Ruby Were We On Again?
The app's Gemfile
and .ruby-version
both indicated that the app was running Ruby 2.3.4, but the app also used an older version of the AuthLogic gem that relied on the unpack1
method, which wasn't available until Ruby 2.4. Instead of upgrading Ruby just to get that method (which could have introduced other side effects), we added a temporary monkey-patch to add that method as a initializer:
# config/initializers/unpack_patch.rb
unless String.instance_methods.include?(:unpack1)
class String
def unpack1(format)
unpack(format).first
end
end
end
More AuthLogic Shenanigans
AuthLogic wasn't done with us yet. It was trying to call a with_scope
method, which was removed after Rails 3.1. The best choice in this case was to simply upgrade AuthLogic to a later minor version (3.3 to 3.6) that didn't call that missing method anymore.
Success!
And that did it! With a dev containers, and a few minor app code tweaks, we were able to get this very old app running on modern hardware.