From 4f76d12874aa3d565b69ba8eea8eb4e991a41a98 Mon Sep 17 00:00:00 2001 From: Mike McQuaid Date: Wed, 20 Feb 2019 12:28:59 +0000 Subject: [PATCH] utils/gems: extract from utils. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also, don’t use any non-standard Ruby functionality to avoid needing any `requires. --- Library/Homebrew/utils.rb | 63 +-------------------------- Library/Homebrew/utils/gems.rb | 79 ++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 62 deletions(-) create mode 100644 Library/Homebrew/utils/gems.rb diff --git a/Library/Homebrew/utils.rb b/Library/Homebrew/utils.rb index aef2f9d9aa..c230acbfb8 100644 --- a/Library/Homebrew/utils.rb +++ b/Library/Homebrew/utils.rb @@ -3,6 +3,7 @@ require "utils/analytics" require "utils/curl" require "utils/fork" require "utils/formatter" +require "utils/gems" require "utils/git" require "utils/github" require "utils/inreplace" @@ -201,68 +202,6 @@ module Homebrew _system(cmd, *args, **options) end - def setup_gem_environment! - # Match where our bundler gems are. - ENV["GEM_HOME"] = "#{ENV["HOMEBREW_LIBRARY"]}/Homebrew/vendor/bundle/ruby/#{RbConfig::CONFIG["ruby_version"]}" - ENV["GEM_PATH"] = ENV["GEM_HOME"] - - # Make rubygems notice env changes. - Gem.clear_paths - Gem::Specification.reset - - # Add Gem binary directory and (if missing) Ruby binary directory to PATH. - path = PATH.new(ENV["PATH"]) - path.prepend(RUBY_BIN) if which("ruby") != RUBY_PATH - path.prepend(Gem.bindir) - ENV["PATH"] = path - end - - # TODO: version can be removed when compat/download_strategy is deleted in 2.0 - def install_gem!(name, version = nil) - setup_gem_environment! - - return unless Gem::Specification.find_all_by_name(name, version).empty? - - ohai "Installing or updating '#{name}' gem" - install_args = %W[--no-document #{name}] - install_args << "--version" << version if version - - # Do `gem install [...]` without having to spawn a separate process or - # having to find the right `gem` binary for the running Ruby interpreter. - require "rubygems/commands/install_command" - install_cmd = Gem::Commands::InstallCommand.new - install_cmd.handle_options(install_args) - exit_code = 1 # Should not matter as `install_cmd.execute` always throws. - begin - install_cmd.execute - rescue Gem::SystemExitException => e - exit_code = e.exit_code - end - odie "Failed to install/update the '#{name}' gem." if exit_code.nonzero? - end - - def install_gem_setup_path!(name, executable: name) - install_gem!(name) - - return if which(executable) - - odie <<~EOS - The '#{name}' gem is installed but couldn't find '#{executable}' in the PATH: - #{ENV["PATH"]} - EOS - end - - def install_bundler! - install_gem_setup_path! "bundler", executable: "bundle" - end - - def install_bundler_gems! - install_bundler! - ENV["BUNDLE_GEMFILE"] = "#{HOMEBREW_LIBRARY_PATH}/test/Gemfile" - system "bundle", "install" unless quiet_system("bundle", "check") - setup_gem_environment! - end - # rubocop:disable Style/GlobalVars def inject_dump_stats!(the_module, pattern) @injected_dump_stat_modules ||= {} diff --git a/Library/Homebrew/utils/gems.rb b/Library/Homebrew/utils/gems.rb new file mode 100644 index 0000000000..40d0761c3c --- /dev/null +++ b/Library/Homebrew/utils/gems.rb @@ -0,0 +1,79 @@ +# Never `require` anything in this file (except English). It needs to be able to +# work as the first item in `brew.rb` so we can load gems with Bundler when +# needed before anything else is loaded (e.g. `json`). + +require "English" + +module Homebrew + module_function + + def ruby_bindir + "#{RbConfig::CONFIG["prefix"]}/bin" + end + + def setup_gem_environment! + # Match where our bundler gems are. + ENV["GEM_HOME"] = "#{ENV["HOMEBREW_LIBRARY"]}/Homebrew/vendor/bundle/ruby/#{RbConfig::CONFIG["ruby_version"]}" + ENV["GEM_PATH"] = ENV["GEM_HOME"] + + # Make RubyGems notice environment changes. + Gem.clear_paths + Gem::Specification.reset + + # Add necessary Ruby and Gem binary directories to PATH. + paths = ENV["PATH"].split(":") + paths.unshift(ruby_bindir) unless paths.include?(ruby_bindir) + paths.unshift(Gem.bindir) unless paths.include?(Gem.bindir) + ENV["PATH"] = paths.compact.join(":") + end + + def install_gem!(name, version = nil) + setup_gem_environment! + return unless Gem::Specification.find_all_by_name(name, version).empty? + + # Shell out to `gem` to avoid RubyGems requires e.g. loading JSON. + puts "==> Installing '#{name}' gem" + install_args = %W[--no-document #{name}] + install_args << "--version" << version if version + return if system "#{ruby_bindir}/gem", "install", *install_args + + $stderr.puts "Error: failed to install the '#{name}' gem." + exit 1 + end + + def install_gem_setup_path!(name, executable: name) + install_gem!(name) + return if ENV["PATH"].split(":").any? do |path| + File.executable?("#{path}/#{executable}") + end + + $stderr.puts <<~EOS + Error: the '#{name}' gem is installed but couldn't find '#{executable}' in the PATH: + #{ENV["PATH"]} + EOS + exit 1 + end + + def install_bundler! + install_gem_setup_path! "bundler", executable: "bundle" + end + + def install_bundler_gems! + install_bundler! + + ENV["BUNDLE_GEMFILE"] = "#{ENV["HOMEBREW_LIBRARY"]}/Homebrew/test/Gemfile" + @bundle_installed ||= begin + bundle_check_output = `#{Gem.bindir}/bundle check` + bundle_check_failed = !$CHILD_STATUS.exitstatus.zero? + + # for some reason sometimes the exit code lies so check the output too. + if bundle_check_failed || bundle_check_output.include?("Install missing gems") + system "#{Gem.bindir}/bundle", "install" + else + true + end + end + + setup_gem_environment! + end +end