A couple of years ago, I had a dream: to make it dead-easy to distribute Ruby CLI apps to end users, without requiring those users to install Ruby or muck about with gems and Bundler. And thus Traveling Ruby was born.
Traveling Ruby hasn't seen updates for quite a while now. Recently I tried making a new bugfix release, but I found it to be more challenging than I had hoped. In this article I reflect on those challenges, as well as on the future of Traveling Ruby.
The dream of a single, self-contained package
Back in the 90s, I wrote Windows desktop apps with Delphi. Unlike its contemporary competitors such as Visual Basic and Java, which generated apps that required users to install a runtime, Delphi produced a single .exe that just works. Fast forward to the mid 2010s, and we saw that Go – which could also produce a single executable that required no runtime – was gaining popularity. Sometimes at the expense of Ruby.
While Traveling Ruby doesn't produce a single executable, it allows you to package your Ruby app as a single self-contained directory, which is good enough.
What's needed: sustainability and democratization
Traveling Ruby hasn't seen updates for quite a while now, but it's still being used. Yesterday, the author of the Pact contract testing framework asked me to do a new release, because he needed a bug fix for supporting paths that contain spaces. So I gave it a try. Predictably, the build system is broken due to all the changes in Linux and macOS the past few years, and fixing it all takes a lot of effort.
However, this is not a sustainable. The reason why Traveling Ruby stopped getting updates after a while is because it's too dependent on a single maintainer: myself. The main contributing factor to why there's only a single maintainer, is that maintaining Traveling Ruby requires expert knowledge on C-based build systems and binary compatibility for both Linux and macOS. This is a low-level skill that's not commonly found in the Ruby community.
The same issue potentially threatens Fullstaq Ruby, which is my current main focus. Fullstaq Ruby also requires skills in the areas of C-based build systems, and OS packaging. This time I've learned my lesson, so I seek to make Fullstaq Ruby sustainable through these strategies:
- Automation of as many maintenance tasks as possible.
- Knowledge sharing through documentation. Allow anyone to learn the required skills, and clearly describe all the maintenance procedures.
- Actively recruiting more maintainers.
These strategies are not easily implemented, and take time.
I hope to revive Traveling Ruby's vision one day. A successor project should employ the same strategies to ensure the sustainability of the project. A successor project should aim for democratization: making it easy for anyone to contribute.
In Traveling Ruby's case, macOS seems to be doing everything it can to prevent democratization from being realized.
Building Traveling Ruby requires setting up an isolated, carefully-controlled build environment. In Linux this is easy: spin up a Docker container. MacOS has no native containers: Docker for Mac is merely a Linux VM; containers don't run macOS inside. So we try to set up such an environment by using environment variables. However, some things cannot be isolated, such as random libraries in /usr/local. So this gives maintainers two options:
- While maintaining Traveling Ruby, temporarily clear elements that may pollute the build environment. When done with the maintenance work, restore those elements.
- Set up an entirely new, clean macOS instance, specifically for the purpose of maintaining Traveling Ruby. Preferably, make this a CI server so that we can automate things.
Option 1 is easier than 2, but it's a hassle, and not automated.
Option 2 is very bad too:
- Setting up a macOS CI server is very expensive, money-wise. This is because its license only allows running on Apple hardware. This means buying or renting dedicated Apple hardware, as opposed to spinning up virtualized cloud instances that run on commodity hardware.
- Setting up a macOS CI server is very expensive, time- and effort-wise. Virtualization is a great way to backup, restore and reset the CI server. But macOS is neither a good server OS, nor a good virtualization host. Installing Linux on Apple hardware is not an easy task. Installing macOS in a production-grade virtualizion platform, such as KVM, is also not an easy task.
- Thanks to the restrictive licensing, there are very few hosting providers that offer macOS. MacStadium and Amazon Web Services are all that I can think of. All of them are very expensive.
What about CI providers, such as Azure DevOps and Github Actions? I hoped that they would bring about more democratization. Unfortunately, there are more roadblocks, making those options entirely unfeasible.
macOS's increasingly restrictive security features, conflict with Traveling Ruby's build environment. In particular, we rely on
DYLD_LIBRARY_PATH, but this doesn't work when System Integrity Protection (SIP) is enabled. Disabling SIP on managed CI instances such as Azure DevOps or Github Actions is not possible. I don't know whether disabling SIP on MacStadium and AWS is possible.
This means that we'll need a dedicated Mac machine for maintainers to use. Someone has to buy it, build it and maintain it. This makes that someone a potential bottleneck.
macOS also evolves in much more impactful ways than Linux. This makes knowledge sharing hard, because the knowledge must be ever-evolving. Maintainers must be experts that both understand the underlying issues, as well as keep up with the latest changes, so that they can debug new problems. Finding out that
DYLD_LIBRARY_PATH stopped working due to System Integrity Protection, is an example of an obscure problem that's not easily diagnosed by those with insufficient knowledge of macOS's internals.
Making a successor to Traveling Ruby, that's sustainable and democratized, is no easy task, in large thanks to macOS. I have no easy answers to these challenges: it seems that there's no option but to bite the bullet. I hope that its dream will be fulfilled some day, because I love Ruby.