When I start Ruby, I see that it scans the home directory for user-installed gems:
strace -e file ruby30 -e nil |& fgrep $HOME
It's a long list, have a loook at it yourself. For me, it's roughly 300 system calls.
When I deploy Ruby scripts in my company, I want these Ruby scripts not to access the user home directory. All the gems that are necessary for my deployed Ruby scripts are installed using RPMs, alongside the interpreter.
Problems
It want to prevent these accesses to user home directory for multiple reasons.
Reproducible/correct behavior
The user might have installed a newer version of a gem. The newer version might behave differently from the installed gem. That's I want my deployed Ruby scripts to use the installed gems, and the installed gems only. Reproducible behavior is of paramount importance, because many of these Ruby scripts are used in builds that must be reproducible
Maintenance
Some users user-installed gems using prior versions of Ruby. My deployed scripts use a newer interpreter. Whenever one of these users starts one of my Ruby scripts, they get warnings like this one, at every single start:
Ignoring mysql2-0.4.8 because its extensions are not built. Try: gem pristine mysql2 --version 0.4.8
Performance
The user home directories are one a network share, mounted using NFS. Scanning the home directory via NFS makes Ruby startup much slow.
Approaches
--disable=gems
- (good)
ruby --disable=gemsprevents Ruby from accessing to the user gems. (According tostrace: no access to the user home directory at all.) - (good) It makes Ruby startup roughly 10 times faster (in my setup).
- (good) Requires a simple change in the shebang line:
#!/usr/bin/ruby30 --disable=gems - (bad) It prevents me from loading some gems (e.g.:
nokogiri); I guess this is about a Gem being built-in or not being built-in. - (bad)
--disable=gemsis deprecated for Ruby >=3.1: https://ruby-doc.org/core-3.1.0/NEWS_md.html#label-Command+line+options
Setting $GEM_PATH and $GEM_SPEC_CACHE
(good)
env GEM_PATH= GEM_SPEC_CACHE= rubyprevents Ruby from accessing the user gems. (According tostrace: no access to the user home directory at all.)(good) Ruby startup is much slower than
ruby --disable=gems, but it is still much faster than the default behavior.(good) Ruby is still able to load all gems that are installed using RPMs
(?) I have to create and deploy a wrapper:
#!/bin/sh GEM_PATH= GEM_SPEC_CACHE= exec /usr/bin/ruby30 "$@"All my deployed scripts have to use this wrapper.
Setting Gem.paths
Inserting this snippet right at the start of the script:
#!/usr/bin/ruby
Gem.paths = {
"GEM_HOME" => Gem.paths.home,
"GEM_PATH" => Gem.path.reject { |path| path.start_with? "/home/" }.join(":"),
"SPEC_CACHE_DIR" => ""
}
[...]
- (good) It prevents Ruby from loading the user gems.
- (bad) Ruby accesses (and checks) all user gems before the start of the script.
- (bad) Aesthetics
Comparing with Python
Isn't there a better solution? Something like python -s?