Skip to content

Commit c5f4a85

Browse files
committed
Document design aspects: limited gem dependencies, no gem activation during Ruby loader initialization
[ci skip]
1 parent 253298c commit c5f4a85

File tree

5 files changed

+85
-8
lines changed

5 files changed

+85
-8
lines changed

Gemfile

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1+
# Specifies Passenger *development* dependencies.
2+
# See also: doc/DesignAspects/LimitedGemDependencies.md
3+
14
source 'https://rubygems.org/'
25

36
ruby '>= 2.5'
47

8+
gemspec
9+
510
group :development do
611
gem 'json'
712
gem 'mime-types', '~> 3.5.1'
813
gem 'drake'
9-
gem 'rack'
10-
gem 'rackup', '>= 2.1'
11-
gem 'rake'
1214
gem 'rspec', '~> 3.12.0'
1315
gem 'rspec-collection_matchers'
1416
gem 'webrick', '~> 1.8.1'

Gemfile.lock

+10-5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
PATH
2+
remote: .
3+
specs:
4+
passenger (6.0.25)
5+
rack (>= 1.6.13)
6+
rackup (>= 2.0.0)
7+
rake (>= 12.3.3)
8+
19
GEM
210
remote: https://rubygems.org/
311
specs:
@@ -10,9 +18,8 @@ GEM
1018
mime-types-data (~> 3.2015)
1119
mime-types-data (3.2024.1001)
1220
rack (3.1.8)
13-
rackup (2.1.0)
21+
rackup (2.2.1)
1422
rack (>= 3)
15-
webrick (~> 1.8)
1623
rake (13.2.1)
1724
rspec (3.12.0)
1825
rspec-core (~> 3.12.0)
@@ -41,9 +48,7 @@ DEPENDENCIES
4148
drake
4249
json
4350
mime-types (~> 3.5.1)
44-
rack
45-
rackup (>= 2.1)
46-
rake
51+
passenger!
4752
rspec (~> 3.12.0)
4853
rspec-collection_matchers
4954
webrick (~> 1.8.1)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Limited gem dependencies
2+
3+
## Context
4+
5+
- We provide native packages (APT/YUM) for Passenger.
6+
- Passenger has a bunch of tools (e.g. `bin/passenger-status`, `helper-scripts/backtrace-sanitizer.rb`, `helper-scripts/prespawn`) that are written in Ruby. When Passenger is natively packaged, we ensure that these tools run in the "system" Ruby.
7+
- On Debian-based systems, the OS package repositories may supply multiple Ruby versions. So our native packaging can't assume a single Ruby version.
8+
9+
This raises the question of how to deal with Passenger's own gem dependencies. An analysis of possible strategies and their implications/tradeoffs:
10+
11+
1. If the OS package repositories supply dependent gems, then our native packages can depend on those directly.
12+
13+
Drawbacks:
14+
15+
- Limited selection of gems.
16+
17+
2. We package the gem dependencies ourselves. In addition, any dependency with native extensions must be packaged separately, once for each Ruby version installable through the native OS package repositories.
18+
19+
Drawbacks:
20+
21+
- More work for us.
22+
23+
3. We vendor the gem dependencies within the native packages. In addition, any dependency with native extensions must be vendored separately, once for each Ruby version installable through the native OS package repositories.
24+
25+
## Decision
26+
27+
We pick strategy 1.
28+
29+
## Consequences
30+
31+
- Any gems that the written-in-Ruby tools depend on, must either be available through OS repositories, or must be a Ruby standard library gem.
32+
- All such dependencies must be specified in the Passenger gemspec.
33+
- Development-related Passenger Ruby code can depend on any gem, since they're not included in native packages.
34+
- All such dependencies must be specified in the Passenger Gemfile.
35+
36+
## See also
37+
38+
- [No gem activation during Ruby loader initialization](NoGemActivationDuringRubyLoaderInitialization.md)
39+
- System Ruby runner (only used in Debian packages): `packaging/debian/debian_specs/passenger/passenger_system_ruby.c.erb`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# No gem activation during Ruby loader initialization
2+
3+
## Context
4+
5+
Passenger loads a Ruby app using loader scripts (rack-loader.rb and rack-preloader.rb). These scripts perform these steps:
6+
7+
1. Initialization, e.g., to setup communication with the Passenger Core.
8+
2. Activate Bundler.
9+
3. Load the application (config.ru).
10+
11+
If step 1 activates any gems, then that may conflict with Bundler activation because Gemfile could specify a different gem version.
12+
13+
## Decision
14+
15+
All code callable by the Ruby loaders' initialization steps, must not activate any gems.
16+
17+
## Consequences
18+
19+
Any needed functionality that's typically provided by gems, must be reimplemented within the `PhusionPassenger` module.
20+
21+
For example, the initialization step needs to parse JSON. Instead of activating the JSON gem, we reimplement a JSON parser in `PhusionPassenger::Utils::JSON`.
22+
23+
### Caveat: standard library gems
24+
25+
More and more parts of the Ruby standard library are becoming [gemified](https://stdgems.org/). This increases the chances of the initialization step activating a standard library gem. In principle, our strategy remains the same: we reimplement those libraries. For example, we reimplemented StringScanner (needed by our JSON implementation) in `PhusionPassenger::Utils::StringScanner`, but we limited its scope to just the functionality needed by the JSON implementation.
26+
27+
If the standard library in question is a native extension (e.g., `etc`), then the strategy is to reimplement it in C++ as part of the [Passenger Agent binary](../../src/agent/README.md), as a separately callable tool. The Ruby code then spawns the written-in-C++ tool and parses its output.

passenger.gemspec

+4
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,13 @@ Gem::Specification.new do |s|
2323
"mailing_list_uri" => "https://www.phusionpassenger.com/contact",
2424
"wiki_uri" => "https://github.com/phusion/passenger/wiki"
2525
}
26+
27+
# Limit dependencies only to those available through OS package repositories.
28+
# See doc/DesignAspects/LimitedGemDependencies.md
2629
s.add_dependency 'rake', '>= 12.3.3'
2730
s.add_dependency 'rack', '>= 1.6.13'
2831
s.add_dependency 'rackup', '>= 2.0.0'
32+
2933
s.files = Dir[*PhusionPassenger::Packaging::GLOB] -
3034
Dir[*PhusionPassenger::Packaging::EXCLUDE_GLOB]
3135
s.executables = PhusionPassenger::Packaging::USER_EXECUTABLES +

0 commit comments

Comments
 (0)