This is a system for building portable Linux and macOS binaries for the Passenger web app server -- binaries that work on a wide range of Linux distributions and macOS versions. This is achieved as follows:
- By statically linking any non-system dependencies.
- On Linux: by compiling against an older glibc. See Holy Build Box for more information.
- On macOS: by compiling against a sufficiently old deployment target version.
Phusion uses this system to build Passenger binaries, immediately following a source release, in a completely automated manner.
Table of contents
This system builds the following binaries:
- The Passenger agent.
- Passenger Ruby native support extensions, for multiple Ruby versions.
- Nginx.
The Nginx version that will be compiled is the version preferred by the Passenger codebase, but you can override the exact version that is to be built. The Nginx binary includes the following modules:
http_ssl_module
http_v2_module
http_gzip_static_module
http_proxy_module
http_fastcgi_module
http_scgi_module
http_uwsgi_module
http_status_stub_module
http_addition_module
http_geoip_module
http_realip_module
Learn more at: How it works
Use the linux/build
script to build binaries for Linux. Docker is required. Here is an example:
$ cd linux
$ ./build \
-p /path-to-passenger-source \
-c cache \
-o output \
-A amd64 \
passenger nginx
The build
script expects at least the following arguments:
-p
: path to the Passenger source code that you want to build.-c
: a directory which the script can use for caching data, in order to make subsequent builds faster.-o
: a directory to store the built binaries in.-A
: the Docker architecture to build in. Eitheramd64
orarm64
.- And finally, a list of things that the script should build (tasks). In this example we specified two tasks,
passenger
andnginx
. The passenger task builds the Passenger agent and all Ruby extensions, while the nginx task builds Nginx.
More command line options are available. Run ./build -h
to learn more. You can also run ./build -T
to learn which tasks are available.
When the above example build is finished, the output directory will contain these files:
ruby-extensions/ruby-2.1.10-x86_64-linux/passenger_native_support.so
ruby-extensions/ruby-2.2.10-x86_64-linux/passenger_native_support.so
ruby-extensions/ruby-2.3.8-x86_64-linux/passenger_native_support.so
ruby-extensions/ruby-2.4.5-x86_64-linux/passenger_native_support.so
ruby-extensions/ruby-2.5.5-x86_64-linux/passenger_native_support.so
ruby-extensions/ruby-2.6.2-x86_64-linux/passenger_native_support.so
support-binaries/PassengerAgent
support-binaries/nginx-1.15.8
Before you can build binaries for macOS, you must ensure that these prerequities are (manually) installed:
- Xcode command-line developer tools.
- GPG.
- RVM.
- Not using RVM, and instead using rbenv or something? For the sake of simplicity,
passenger_binary_build_automation
only supports RVM. We recommend that you create a new account on your Mac that uses RVM only, and to developpassenger_binary_build_automation
on that account.
- Not using RVM, and instead using rbenv or something? For the sake of simplicity,
- All Ruby versions specified in the
shared/definitions/ruby_versions
file.
Once these prerequisites are installed, you must build a runtime using the macos/setup-runtime
script. A runtime is a directory containing further tools and libraries that passenger_binary_build_automation
needs for building binaries (see also How it works section "The build environment"). The runtime needs to be setup the first time you use passenger_binary_build_automation
, every time the list of libraries that we link into Passenger changes, and every time Nginx changes.
Here is an example invocation:
$ cd macos
$ ./setup-runtime -c cache -o runtime-output
The setup-runtime
script expects at least the following arguments:
-c
: a directory which the script can use for caching data, in order to make subsequent runtime builds faster.-o
: a directory to store the runtime in. When testing things locally, this can be any arbitrary directory of your chosing but may not contain spaces (but see also Where is the runtime directory? (macOS) to find out where it is on the CI server).
Use the macos/build
script to build binaries for macOS. Here is an example:
$ cd macos
$ ./build \
-r runtime-output \
-p /path-to-passenger-source \
-c cache \
-o output \
passenger nginx
The build
script expects at least the following arguments:
-r
: path to the directory in which the runtime is stored. This must be the same path that you passed tomacos/setup-runtime
through-o
.-p
: path to the Passenger source code that you want to build.-c
: a directory which the script can use for caching data, in order to make subsequent builds faster.-o
: a directory to store the built binaries in.- And finally, a list of things that the script should build (tasks). In this example we specified two tasks,
passenger
andnginx
. The passenger task builds the Passenger agent and all Ruby extensions, while the nginx task builds Nginx.
More command line options are available. Run ./build -h
to learn more. You can also run ./build -T
to learn which tasks are available.
When the above example build is finished, the output directory will contain these files:
support-binaries/nginx-1.15.8
support-binaries/PassengerAgent
The macOS build script does not build Ruby native extensions because we haven't yet figured out a way to do that in a portable manner.
passenger_binary_build_automation
builds native extensions for a select number of Ruby versions. If a new version of Ruby has been released then we should re-evaluate which Ruby versions to build extensions for.
The policy is to build native extensions for the latest patchlevel version of all minor Ruby versions that are somewhat widespread in use. For example, as of 15 Mar 2019, the list is: 2.1.10, 2.2.10, 2.3.8, 2.4.5, 2.5.5, 2.6.2. Suppose that Ruby 2.5.6 is released one day later (just look what happened with 2.5.5), then we should build against 2.1.10, 2.2.10, 2.3.8, 2.4.5, 2.5.6 (dropping 2.5.5), and 2.6.2. Suppose a year later, 2.7.0 is released and we believe that 2.1 is no longer in widespread use. Then we can change the list to: 2.2.10, 2.3.8, 2.4.5, 2.5.5, 2.6.2, 2.7.0.
The procedure for updating the list of Ruby versions to build against, is as follows:
- Change the file
shared/definitions/ruby_versions
accordingly. One Ruby version number per line. - Update the Docker container and the macOS runtime.
- Build the binaries locally to test whether everything works as expected.
- Update the
passenger_binary_build_automation
version lock in Passenger to the currentpassenger_binary_build_automation
commit. - Publish binaries.
passenger_binary_build_automation
does not control which Nginx version to build. passenger_binary_build_automation
builds the Nginx version set the PREFERRED_NGINX_VERSION
constant in the Passenger source code. So if you want to upgrade Nginx then change that constant in the Passenger source code.
passenger_binary_build_automation
statically links a number of libraries into the Passenger agent and Nginx. These libraries need to be updated once in a while, e.g. when important bugs or security vulnerabilities have been fixed. The procedure for doing that is as follows.
- Change the relevant library version numbers in
shared/definitions
(there's a handy script at./update.sh
). - Update the Docker container and the macOS runtime.
- Build the binaries locally to test whether everything works as expected.
- Update the
passenger_binary_build_automation
version lock in Passenger to the currentpassenger_binary_build_automation
commit. - Publish binaries.
As described in How it works, passenger_binary_build_automation
works through a Docker container (Linux) or a runtime environment (macOS). Sometimes you may want to update this container or runtime, e.g. because you want to update libraries and depencies. The procedure for updating the Docker container (rebuilding and republishing it), and for rebuilding the macOS runtime, is as follows:
-
Bump the version number in
shared/definitions/docker_image_version
. -
If you changed anything in the macOS runtime besides bumping Ruby version numbers (e.g. you updated OpenSSL or something), then also bump the version number in
shared/definitions/macos_runtime_version
. -
Rebuild the Docker container. On a Linux machine:
- Run
./linux/setup-docker-images
- Publish the new Docker container to the Docker Hub: run
./linux/publish-docker-images
- Run
-
If you changed the ruby versions at all, then you need to update the versions in the ansible playbook for the passenger ci cluster, and then deploy to production:
./run production --ask-vault-pass -l macos_slave_vm
. -
If you bumped
shared/definitions/macos_runtime_version
, then (locally) rebuild the runtime (see Building binaries / For macOS / Preparation) to see whether it works.Note: there is no need to manually rebuild the runtime (or to manually remove the runtime) on the Passenger CI server. The CI job will automatically build a new runtime whenever it detects that
macos_runtime_version
has changed. This is because the runtime directory on the CI server contains the runtime version in its path. See also Where is the runtime directory? (macOS). -
Git commit and push.
As described in How it works, the Passenger Git repository locks down to a specific version of passenger_binary_build_automation
using the Git submodule system. Any changes in passenger_binary_build_automation
does not take effect until you update the lock inside Passenger by bumping the submodule pointer. The procedure for doing that is as follows:
- In the Passenger Git repository, switch to the branch that is eligible for the next release (
stable-5.1
at the time of writing). - On that branch, update the
packaging/binaries
submodule to thepassenger_binary_build_automation
commit you want. - Git commit and push Passenger.
- In the Passenger Enterprise Git repository, switch to the branch that is eligible for the next release (
stable-5.1
at the time of writing). - Merge the open source Passenger branch that you committed to, into the current Passenger Enterprise branch (e.g.
git merge oss/stable-5.1
), so that Passenger Enterprise is also locked down against thispassenger_binary_build_automation
commit. - Git commit and push Passenger Enterprise.
If you have made a modification to passenger_binary_build_automation
, then building and publishing new binaries involves running the Passenger Release Process CI job. There are two variants of this process that you can choose from:
-
Release a new Passenger version. This is the process variant that you will usually be interested in. Or:
-
Update the binaries for an existing Passenger version. One reason for choosing this process variant is: you've found out that the binaries you built for an existing Passenger version contains vulnerabilities (e.g. statically linked to an old OpenSSL) and you just want to update the binaries without releasing a new Passenger version.
- Note that some users may already have downloaded old binaries. Releasing a new version automatically signals to those users that they should upgrade, but if you choose this latter process variant then there is no such signal to those users. You will need to think about how to communicate to those users to update their binaries.
- Commit and push any changes in
passenger_binary_build_automation
. - Go the Passenger Git repository. Switch to the branch that is eligible for the next release. On that branch, update the
passenger_binary_build_automation
version lock in Passenger to the currentpassenger_binary_build_automation
commit if you haven't already. Do the same thing for Passenger Enterprise. - Release a new Passenger version per the usual Passenger release procedure.
First update open source:
- Commit and push any changes in
passenger_binary_build_automation
. - Go to the Passenger Git repository. Checkout the Git tag associated with the Passenger version for which you want to update binaries. For example:
git checkout release-5.1.11
- Create a branch and give it an appropriate name. For example:
git branch hotfix-5.1.11-oss-binaries-update
- On this branch, update the
packaging/binaries
submodule to the currentpassenger_binary_build_automation
commit. - Git commit and push this branch.
Then update Enterprise:
- Go to the Passenger Enterprise Git repository. Checkout the Git tag associated with the Passenger Enterprise version for which you want to update binaries. For example:
git checkout enterprise-5.1.11
- Create a branch and give it an appropriate name. For example:
git branch hotfix-5.1.11-enterprise-binaries-update
- Merge with the open source branch you created in step 3. For example:
git merge oss/hotfix-5.1.11-oss-binaries-update
- Git commit and push this branch.
Finally, update and run the release process:
-
Go to the passenger-release Git repository. Checkout the Git tag associated with the Passenger version for which you want to update binaries. For example:
git checkout release-5.1.11
-
Create a branch and give it an appropriate name. For example:
git branch hotfix-5.1.11-binaries-update
-
On this branch, update the
components/passenger
andcomponents/passenger-enterprise
submodules to the branches that you published in steps 5 and 9, respectively. -
Git commit and push.
-
On the Phusion Jenkins interface, go to the Passenger Release Process job and click on Configure.
-
Scroll down. Update the
Pipeline -> SCM: Git -> Branches to build
field to the passenger-release branch you just published. For example:*/hotfix-5.1.11-binaries-update
. -
Run the Passenger Release Process job. Settings:
- Testing: checked.
- CleanSlate: unchecked if your last run was against the the same Passenger version; checked otherwise or if you are unsure.
- ForceRepublish: checked.
-
If step 16 succeeded, rerun it with Testing unchecked.
-
Go to the Passenger Release Process job and click on Configure. Revert the
Pipeline -> SCM: Git -> Branches to build
field to the original value, which should be*/master
.
passenger_binary_build_automation
supports placing the runtime at any arbitrary location on the filesystem (scripts such as macos/setup-runtime
and macos/build
requires a parameter that specifies where the runtime is). Thus, the location of the runtime is not dictated by passenger_binary_build_automation
, but by the person or the system using passenger_binary_build_automation
.
During local development, the path is entirely chosen by the developer.
During the Passenger release process CI job, the runtime is located on the macOS CI server at /opt/data/jenkins/cache/Passenger-Release-Process/generic-macos-binaries/{passenger,passenger-enterprise}/runtime-$MACOS_RUNTIME_VERSION
. This location is passed to the passenger_binary_build_automation
scripts in the passenger-release
project, as follows:
stages/build-artifacts/build-generic-macos-binaries/jenkinsfile_helper.groovy
sets aCACHE_DIR
environment variable.jenkinsfile_helper.groovy
calls./stages/build-artifacts/build-generic-macos-binaries/{initialize.sh,build.sh}
- These two scripts call
passenger_binary_build_automation
'smacos/{setup-runtime,build}
, respectively, telling them through parameters that the runtime can be found in$CACHE_DIR/runtime
.