From 946f6fe6ec6e44b85ab217e0a0e524ae21aac85b Mon Sep 17 00:00:00 2001 From: Christian Gregg Date: Mon, 8 Feb 2021 23:24:44 +0000 Subject: [PATCH] Add bootchecking for rails applications Adds "RailsBootcheck" to the Rails buildpack. RailsBootcheck attempts to load your rails application and will fail the the build of your application if it is unable to load. This can help prevent scenarios where you can accidentally ship a version of your application that cannot boot properly. Which will lead to errors as the application is released and traffic is routed to it. You can opt-in to this feature via the `HEROKU_RAILS_BOOTCHECK_ENABLE` variable, new rails applications will have this variable set by default. You can opt-out of this feature via the `HEROKU_RAILS_BOOTCHECK_DISABLE` variable. Closes #1120 --- lib/language_pack/helpers/rails_bootcheck.rb | 50 ++++++++++++++++++++ lib/language_pack/rails2.rb | 12 ++++- 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 lib/language_pack/helpers/rails_bootcheck.rb diff --git a/lib/language_pack/helpers/rails_bootcheck.rb b/lib/language_pack/helpers/rails_bootcheck.rb new file mode 100644 index 000000000..9227f3039 --- /dev/null +++ b/lib/language_pack/helpers/rails_bootcheck.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +class LanguagePack::Helpers::RailsBootcheck + include LanguagePack::ShellHelpers + + def initialize(timeout = 65) + @timeout = timeout + end + + def call + return unless opted_in? && !opted_out? + + topic("Bootchecking rails application") + + process = ProcessSpawn.new( + "rails runner 'puts Rails.env'", + user_env: true, + timeout: @timeout, + file: "./.heroku/ruby/compile/rails_bootcheck.txt" + ) + + if process.timeout? + failure("timeout", process.output) + elsif !process.success? + failure("failure", process.output) + end + end + + private + + def failure(type, output) + message = String.new("Bootchecking rails application #{type}\n") + message << "set HEROKU_RAILS_BOOTCHECK_DISABLE=1 to disable this feature\n" + + if !output.empty? + message << "\n" + message << output + end + + error(message) + end + + def opted_in? + env("HEROKU_RAILS_BOOTCHECK_ENABLE") + end + + def opted_out? + env("HEROKU_RAILS_BOOTCHECK_DISABLE") + end +end diff --git a/lib/language_pack/rails2.rb b/lib/language_pack/rails2.rb index 75177f6ad..574a45a12 100644 --- a/lib/language_pack/rails2.rb +++ b/lib/language_pack/rails2.rb @@ -1,6 +1,7 @@ require "fileutils" require "language_pack" require "language_pack/rack" +require "language_pack/helpers/rails_bootcheck" # Rails 2 Language Pack. This is for any Rails 2.x apps. class LanguagePack::Rails2 < LanguagePack::Ruby @@ -19,6 +20,7 @@ def self.use? def initialize(*args) super(*args) @rails_runner = LanguagePack::Helpers::RailsRunner.new + @bootcheck = LanguagePack::Helpers::RailsBootcheck.new end def name @@ -29,7 +31,7 @@ def default_env_vars { "RAILS_ENV" => "production", "RACK_ENV" => "production" - } + }.merge(bootcheck_env_vars) end def default_config_vars @@ -60,6 +62,7 @@ def compile instrument "rails2.compile" do install_plugins super + @bootcheck.call end end @@ -106,4 +109,11 @@ def setup_profiled(*args) end end + def bootcheck_env_vars + value = env("HEROKU_RAILS_BOOTCHECK_ENABLE") + + return {} unless value || new_app? + + { "HEROKU_RAILS_BOOTCHECK_ENABLE" => value || "1" } + end end