summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHomu <homu@barosl.com>2016-06-11 12:03:38 +0900
committerHomu <homu@barosl.com>2016-06-11 12:03:38 +0900
commit13f64fa210edbf92f3281e4055e88a37437d0486 (patch)
tree71df1d14b0f81c1c1d17c41e06c295f46f0c0f3e
parent5a80fcfea13c97baad4935df9c323fb67d499877 (diff)
parentdeb5a149134a6366de70187a85ae67d582397797 (diff)
downloadbundler-13f64fa210edbf92f3281e4055e88a37437d0486.tar.gz
Auto merge of #4643 - bundler:seg-postit-trampoline, r=indirect
[bundle] Automatically trampoline to postit - [x] Specs ~~Except on bundle exec, since that would be too slow~~ ~~If you want, I guess we could vendor postit and manually setup the `LOAD_PATH` and `-r` the full path to the vendored postit exe. Your call.~~ Done this \c @indirect
-rw-r--r--Rakefile11
-rwxr-xr-xexe/bundle7
-rw-r--r--lib/bundler/cli.rb2
-rw-r--r--lib/bundler/cli/update.rb2
-rw-r--r--lib/bundler/postit_trampoline.rb54
-rw-r--r--lib/bundler/setup.rb1
-rw-r--r--lib/bundler/shared_helpers.rb1
-rw-r--r--lib/bundler/vendor/postit/lib/postit.rb15
-rw-r--r--lib/bundler/vendor/postit/lib/postit/environment.rb44
-rw-r--r--lib/bundler/vendor/postit/lib/postit/installer.rb28
-rw-r--r--lib/bundler/vendor/postit/lib/postit/parser.rb21
-rw-r--r--lib/bundler/vendor/postit/lib/postit/setup.rb12
-rw-r--r--lib/bundler/vendor/postit/lib/postit/version.rb3
-rw-r--r--spec/bundler/shared_helpers_spec.rb2
-rw-r--r--spec/other/trampoline_spec.rb137
-rw-r--r--spec/support/helpers.rb1
-rw-r--r--spec/support/rubygems_ext.rb3
17 files changed, 341 insertions, 3 deletions
diff --git a/Rakefile b/Rakefile
index 43c5c58237..9bc34e4177 100644
--- a/Rakefile
+++ b/Rakefile
@@ -296,10 +296,21 @@ begin
lib.prefix = "Bundler"
lib.vendor_lib = "lib/bundler/vendor/thor"
end
+
+ Automatiek::RakeTask.new("postit") do |lib|
+ lib.download = { :github => "https://github.com/bundler/postit" }
+ lib.namespace = "PostIt"
+ lib.vendor_lib = "lib/bundler/vendor/postit"
+
+ def lib.namespace_files
+ process_files(namespace, "BundlerVendoredPostIt")
+ end
+ end
rescue LoadError
namespace :vendor do
task(:molinillo) { abort "Install the automatiek gem to be able to vendor gems." }
task(:thor) { abort "Install the automatiek gem to be able to vendor gems." }
+ task(:postit) { abort "Install the automatiek gem to be able to vendor gems." }
end
end
diff --git a/exe/bundle b/exe/bundle
index 3fb4ccfce9..51a9035d97 100755
--- a/exe/bundle
+++ b/exe/bundle
@@ -4,6 +4,13 @@
# Exit cleanly from an early interrupt
Signal.trap("INT") { exit 1 }
+unless ENV["BUNDLE_DISABLE_POSTIT"]
+ update = "update".start_with?(ARGV.first || " ") && ARGV.find {|a| a.start_with?("--bundler") }
+ update &&= update =~ /--bundler(?:=(.+))?/ && $1 || "> 0.a"
+ ENV["BUNDLER_VERSION"] = update if update
+ require "bundler/postit_trampoline"
+end
+
require "bundler"
# Check if an older version of bundler is installed
$LOAD_PATH.each do |path|
diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb
index 8585142352..c3a9a01e2b 100644
--- a/lib/bundler/cli.rb
+++ b/lib/bundler/cli.rb
@@ -204,6 +204,8 @@ module Bundler
"Force downloading every gem."
method_option "ruby", :type => :boolean, :banner =>
"Update ruby specified in Gemfile.lock"
+ method_option "bundler", :type => :string, :lazy_default => "> 0.a", :banner =>
+ "Update the locked version of bundler"
def update(*gems)
require "bundler/cli/update"
Update.new(options, gems).run
diff --git a/lib/bundler/cli/update.rb b/lib/bundler/cli/update.rb
index 037da2915e..33b0557fec 100644
--- a/lib/bundler/cli/update.rb
+++ b/lib/bundler/cli/update.rb
@@ -13,7 +13,7 @@ module Bundler
sources = Array(options[:source])
groups = Array(options[:group]).map(&:to_sym)
- if gems.empty? && sources.empty? && groups.empty? && !options[:ruby]
+ if gems.empty? && sources.empty? && groups.empty? && !options[:ruby] && !options[:bundler]
# We're doing a full update
Bundler.definition(true)
else
diff --git a/lib/bundler/postit_trampoline.rb b/lib/bundler/postit_trampoline.rb
new file mode 100644
index 0000000000..7406612e5b
--- /dev/null
+++ b/lib/bundler/postit_trampoline.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+postit_lib = File.expand_path("../vendor/postit/lib", __FILE__)
+$:.unshift(postit_lib)
+require "postit"
+require "rubygems"
+
+environment = BundlerVendoredPostIt::Environment.new([])
+version = Gem::Requirement.new(environment.bundler_version)
+
+installed_version =
+ if defined?(Bundler::VERSION)
+ Bundler::VERSION
+ else
+ File.read(File.expand_path("../version.rb", __FILE__)) =~ /VERSION = "(.+)"/
+ $1
+ end
+installed_version &&= Gem::Version.new(installed_version)
+
+if !version.satisfied_by?(installed_version)
+ begin
+ installer = BundlerVendoredPostIt::Installer.new(version)
+ installer.install!
+ rescue => e
+ abort <<-EOS.strip
+Installing the inferred bundler version (#{version}) failed.
+If you'd like to update to the current bundler version (#{installed_version}) in this project, run `bundle update --bundler`.
+The error was: #{e}
+ EOS
+ end
+
+ Gem.loaded_specs.delete("bundler") unless defined?(Bundler)
+ gem "bundler", version
+ $:.delete(File.expand_path("../..", __FILE__))
+else
+ begin
+ gem "bundler", version
+ rescue LoadError
+ $:.unshift(File.expand_path("../..", __FILE__))
+ end
+end
+
+running_version = begin
+ require "bundler/version"
+ Bundler::VERSION
+rescue LoadError, NameError
+ nil
+end
+
+if !Gem::Version.correct?(running_version.to_s) || !version.satisfied_by?(Gem::Version.create(running_version))
+ abort "The running bundler (#{running_version}) does not match the required `#{version}`"
+end
+
+$:.delete_at($:.find_index(postit_lib))
diff --git a/lib/bundler/setup.rb b/lib/bundler/setup.rb
index 9aae6478cd..c888dabf2c 100644
--- a/lib/bundler/setup.rb
+++ b/lib/bundler/setup.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+require "bundler/postit_trampoline" unless ENV["BUNDLE_DISABLE_POSTIT"]
require "bundler/shared_helpers"
if Bundler::SharedHelpers.in_bundle?
diff --git a/lib/bundler/shared_helpers.rb b/lib/bundler/shared_helpers.rb
index e21d616ecd..01f1118d7b 100644
--- a/lib/bundler/shared_helpers.rb
+++ b/lib/bundler/shared_helpers.rb
@@ -169,6 +169,7 @@ module Bundler
# Set BUNDLE_GEMFILE
ENV["BUNDLE_GEMFILE"] = find_gemfile.to_s
+ ENV["BUNDLER_VERSION"] = Bundler::VERSION
end
def set_path
diff --git a/lib/bundler/vendor/postit/lib/postit.rb b/lib/bundler/vendor/postit/lib/postit.rb
new file mode 100644
index 0000000000..4b3ff34d49
--- /dev/null
+++ b/lib/bundler/vendor/postit/lib/postit.rb
@@ -0,0 +1,15 @@
+require 'postit/environment'
+require 'postit/installer'
+require 'postit/parser'
+require 'postit/version'
+require 'rubygems'
+
+module BundlerVendoredPostIt
+ def self.setup
+ load File.expand_path('../postit/setup.rb', __FILE__)
+ end
+
+ def self.bundler_version
+ defined?(Bundler::VERSION) && Bundler::VERSION
+ end
+end
diff --git a/lib/bundler/vendor/postit/lib/postit/environment.rb b/lib/bundler/vendor/postit/lib/postit/environment.rb
new file mode 100644
index 0000000000..b758fa0ccb
--- /dev/null
+++ b/lib/bundler/vendor/postit/lib/postit/environment.rb
@@ -0,0 +1,44 @@
+require 'postit/parser'
+
+module BundlerVendoredPostIt
+ class Environment
+ def initialize(argv)
+ @argv = argv
+ end
+
+ def env_var_version
+ ENV['BUNDLER_VERSION']
+ end
+
+ def cli_arg_version
+ return unless str = @argv.first
+ str = str.dup.force_encoding('BINARY') if str.respond_to?(:force_encoding)
+ if Gem::Version.correct?(str)
+ @argv.shift
+ str
+ end
+ end
+
+ def gemfile
+ ENV['BUNDLE_GEMFILE'] || 'Gemfile'
+ end
+
+ def lockfile
+ File.expand_path case File.basename(gemfile)
+ when 'gems.rb' then gemfile.sub(/\.rb$/, gemfile)
+ else "#{gemfile}.lock"
+ end
+ end
+
+ def lockfile_version
+ BundlerVendoredPostIt::Parser.new(lockfile).parse
+ end
+
+ def bundler_version
+ @bundler_version ||= begin
+ env_var_version || cli_arg_version ||
+ lockfile_version || "#{Gem::Requirement.default}.a"
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/postit/lib/postit/installer.rb b/lib/bundler/vendor/postit/lib/postit/installer.rb
new file mode 100644
index 0000000000..4139038df8
--- /dev/null
+++ b/lib/bundler/vendor/postit/lib/postit/installer.rb
@@ -0,0 +1,28 @@
+module BundlerVendoredPostIt
+ class Installer
+ def initialize(bundler_version)
+ @bundler_version = bundler_version
+ end
+
+ def installed?
+ if Gem::Specification.respond_to?(:find_by_name)
+ !Gem::Specification.find_by_name('bundler', @bundler_version).nil?
+ else
+ requirement = Gem::Requirement.new(@bundler_version)
+ Gem.source_index.gems.values.any? do |s|
+ s.name == 'bundler' && requirement.satisfied_by?(s.version)
+ end
+ end
+ rescue LoadError
+ false
+ end
+
+ def install!
+ return if installed?
+ require 'rubygems/dependency_installer'
+ installer = Gem::DependencyInstaller.new
+ installer.install('bundler', @bundler_version)
+ installer.installed_gems
+ end
+ end
+end
diff --git a/lib/bundler/vendor/postit/lib/postit/parser.rb b/lib/bundler/vendor/postit/lib/postit/parser.rb
new file mode 100644
index 0000000000..98c4a3578f
--- /dev/null
+++ b/lib/bundler/vendor/postit/lib/postit/parser.rb
@@ -0,0 +1,21 @@
+require 'rubygems'
+
+module BundlerVendoredPostIt
+ class Parser
+ def initialize(file)
+ @file = file
+ end
+
+ BUNDLED_WITH =
+ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
+
+ def parse
+ return unless lockfile = File.file?(@file) && File.read(@file)
+ if lockfile =~ BUNDLED_WITH
+ Regexp.last_match(1)
+ else
+ '< 1.10'
+ end
+ end
+ end
+end
diff --git a/lib/bundler/vendor/postit/lib/postit/setup.rb b/lib/bundler/vendor/postit/lib/postit/setup.rb
new file mode 100644
index 0000000000..260edd559d
--- /dev/null
+++ b/lib/bundler/vendor/postit/lib/postit/setup.rb
@@ -0,0 +1,12 @@
+require 'postit/environment'
+require 'postit/installer'
+
+environment = BundlerVendoredPostIt::Environment.new(ARGV)
+version = environment.bundler_version
+
+installer = BundlerVendoredPostIt::Installer.new(version)
+installer.install!
+
+gem 'bundler', version
+
+require 'bundler/version'
diff --git a/lib/bundler/vendor/postit/lib/postit/version.rb b/lib/bundler/vendor/postit/lib/postit/version.rb
new file mode 100644
index 0000000000..1a16501024
--- /dev/null
+++ b/lib/bundler/vendor/postit/lib/postit/version.rb
@@ -0,0 +1,3 @@
+module BundlerVendoredPostIt
+ VERSION = '0.1.2'.freeze
+end
diff --git a/spec/bundler/shared_helpers_spec.rb b/spec/bundler/shared_helpers_spec.rb
index 98f7994f9f..2ad0c98dba 100644
--- a/spec/bundler/shared_helpers_spec.rb
+++ b/spec/bundler/shared_helpers_spec.rb
@@ -222,7 +222,7 @@ describe Bundler::SharedHelpers do
shared_examples_for "ENV['RUBYOPT'] gets set correctly" do
it "ensures -rbundler/setup is at the beginning of ENV['RUBYOPT']" do
subject.set_bundle_environment
- expect(ENV["RUBYOPT"].split(" ").first).to include("-rbundler/setup")
+ expect(ENV["RUBYOPT"].split(" ")).to start_with("-rbundler/setup")
end
end
diff --git a/spec/other/trampoline_spec.rb b/spec/other/trampoline_spec.rb
new file mode 100644
index 0000000000..640c608508
--- /dev/null
+++ b/spec/other/trampoline_spec.rb
@@ -0,0 +1,137 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+describe "bundler version trampolining" do
+ before do
+ ENV["BUNDLE_DISABLE_POSTIT"] = nil
+ FileUtils.rm_rf(system_gem_path)
+ FileUtils.cp_r(base_system_gems, system_gem_path)
+ end
+
+ context "version guessing" do
+ shared_examples_for "guesses" do |version|
+ it "guesses the correct bundler version" do
+ bundle! "--version"
+ expect(out).to eq("Bundler version #{version}")
+
+ if bundled_app("Gemfile").file?
+ bundle! "exec ruby -e 'puts Bundler::VERSION'"
+ expect(out).to eq(version)
+ end
+ end
+ end
+
+ context "with a lockfile" do
+ before do
+ install_gemfile ""
+ lockfile lockfile.sub(Bundler::VERSION, "1.12.0")
+ end
+
+ include_examples "guesses", "1.12.0"
+ end
+
+ context "with BUNDLER_VERSION" do
+ before do
+ ENV["BUNDLER_VERSION"] = "1.12.0"
+ end
+
+ context "with a lockfile" do
+ before { install_gemfile "" }
+ include_examples "guesses", "1.12.0"
+ end
+
+ context "without a lockfile" do
+ include_examples "guesses", "1.12.0"
+ end
+ end
+
+ context "with no hints" do
+ include_examples "guesses", Bundler::VERSION
+ end
+
+ context "with a gemfile and no lockfile" do
+ before do
+ gemfile ""
+ end
+
+ include_examples "guesses", Bundler::VERSION
+ end
+ end
+
+ context "installing missing bundler versions", :realworld => true do
+ before do
+ ENV["BUNDLER_VERSION"] = "1.12.3"
+ if Bundler::RubygemsIntegration.provides?("< 2.6.4")
+ # necessary since we intall with 2.6.4 but the specs can run against
+ # older versions that match againt the "gem" invocation
+ %w(bundle bundler).each do |exe|
+ system_gem_path.join("bin", exe).open("a") do |f|
+ f << %(\ngem "bundler", ">= 0.a"\n)
+ end
+ end
+ end
+ end
+
+ it "guesses & installs the correct bundler version" do
+ expect(system_gem_path.join("gems", "bundler-1.12.3")).not_to exist
+ bundle! "--version"
+ expect(out).to eq("Bundler version 1.12.3")
+ expect(system_gem_path.join("gems", "bundler-1.12.3")).to exist
+ end
+
+ it "fails gracefully when installing the bundler fails" do
+ ENV["BUNDLER_VERSION"] = "9999"
+ bundle "--version", :expect_err => true
+ expect(err).to start_with(<<-E.strip)
+Installing the inferred bundler version (= 9999) failed.
+If you'd like to update to the current bundler version (1.12.5) in this project, run `bundle update --bundler`.
+The error was:
+ E
+ end
+ end
+
+ context "bundle update --bundler" do
+ before do
+ simulate_bundler_version("1.11.1") do
+ install_gemfile ""
+ end
+ end
+
+ it "updates to the specified version" do
+ # HACK: since no released bundler version actually supports this feature!
+ bundle "update --bundler=1.12.0", :expect_err => true
+ expect(out).to include("Unknown switches '--bundler=1.12.0'")
+ end
+
+ it "updates to the specified (running) version" do
+ # HACK: since no released bundler version actually supports this feature!
+ bundle! "update --bundler=#{Bundler::VERSION}"
+ bundle! "--version"
+ expect(out).to eq("Bundler version #{Bundler::VERSION}")
+ end
+
+ it "updates to the running version" do
+ # HACK: since no released bundler version actually supports this feature!
+ bundle! "update --bundler"
+ bundle! "--version"
+ expect(out).to eq("Bundler version #{Bundler::VERSION}")
+ end
+ end
+
+ context "-rbundler/setup" do
+ before do
+ simulate_bundler_version("1.12.0") do
+ install_gemfile ""
+ end
+ end
+
+ it "uses the locked version" do
+ ruby! <<-R
+ require "bundler/setup"
+ puts Bundler::VERSION
+ R
+ expect(err).to be_empty
+ expect(out).to eq("1.12.0")
+ end
+ end
+end
diff --git a/spec/support/helpers.rb b/spec/support/helpers.rb
index ff41dfbfa4..704e2140f5 100644
--- a/spec/support/helpers.rb
+++ b/spec/support/helpers.rb
@@ -12,6 +12,7 @@ module Spec
end
FileUtils.mkdir_p(tmp)
FileUtils.mkdir_p(home)
+ ENV["BUNDLE_DISABLE_POSTIT"] = "1"
Bundler.send(:remove_instance_variable, :@settings) if Bundler.send(:instance_variable_defined?, :@settings)
end
diff --git a/spec/support/rubygems_ext.rb b/spec/support/rubygems_ext.rb
index 64cc41443d..95f596ea79 100644
--- a/spec/support/rubygems_ext.rb
+++ b/spec/support/rubygems_ext.rb
@@ -11,7 +11,8 @@ module Spec
# Rake version has to be consistent for tests to pass
"rake" => "10.0.2",
# 3.0.0 breaks 1.9.2 specs
- "builder" => "2.1.2"
+ "builder" => "2.1.2",
+ "bundler" => "1.12.0",
}
# ruby-graphviz is used by the viz tests
deps["ruby-graphviz"] = nil if RUBY_VERSION >= "1.9.3"