summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Rakefile8
-rw-r--r--lib/bundler.rb5
-rw-r--r--lib/bundler/compact_index_client/updater.rb4
-rw-r--r--lib/bundler/definition.rb47
-rw-r--r--lib/bundler/feature_flag.rb1
-rw-r--r--lib/bundler/lockfile_generator.rb94
-rw-r--r--lib/bundler/settings.rb1
-rw-r--r--lib/bundler/source.rb8
-rw-r--r--lib/bundler/source/git.rb4
-rw-r--r--lib/bundler/source/metadata.rb2
-rw-r--r--lib/bundler/source/path.rb2
-rw-r--r--lib/bundler/source/rubygems.rb2
-rw-r--r--lib/bundler/ssl_certs/certificate_manager.rb2
-rw-r--r--lib/bundler/vendor/fileutils/lib/fileutils.rb1638
-rw-r--r--lib/bundler/vendored_fileutils.rb8
-rw-r--r--man/bundle-config.ronn3
-rw-r--r--spec/bundler/bundler_spec.rb2
-rw-r--r--spec/bundler/settings_spec.rb4
-rw-r--r--spec/bundler/ssl_certs/certificate_manager_spec.rb8
-rw-r--r--spec/commands/pristine_spec.rb11
-rw-r--r--spec/commands/update_spec.rb36
-rw-r--r--spec/install/deploy_spec.rb4
-rw-r--r--spec/install/force_spec.rb6
-rw-r--r--spec/install/gemfile/eval_gemfile_spec.rb6
-rw-r--r--spec/install/gemfile/git_spec.rb5
-rw-r--r--spec/install/gems/native_extensions_spec.rb1
-rw-r--r--spec/install/git_spec.rb4
-rw-r--r--spec/plugins/source/example_spec.rb2
-rw-r--r--spec/runtime/setup_spec.rb9
-rw-r--r--spec/spec_helper.rb2
-rw-r--r--spec/support/artifice/vcr.rb2
-rw-r--r--spec/support/artifice/vcr_cassettes/realworld/api.rubygems.org/api/v1/dependencies-gems=bundler/GET/request (renamed from spec/support/artifice/vcr_cassettes/realworld/api.rubygems.org/api/v1/dependencies?gems=bundler/GET/request)0
-rw-r--r--spec/support/artifice/vcr_cassettes/realworld/api.rubygems.org/api/v1/dependencies-gems=bundler/GET/response (renamed from spec/support/artifice/vcr_cassettes/realworld/api.rubygems.org/api/v1/dependencies?gems=bundler/GET/response)bin13564 -> 13564 bytes
-rw-r--r--spec/support/helpers.rb8
-rw-r--r--spec/update/git_spec.rb5
35 files changed, 1845 insertions, 99 deletions
diff --git a/Rakefile b/Rakefile
index f516673ef1..6f08086731 100644
--- a/Rakefile
+++ b/Rakefile
@@ -340,6 +340,13 @@ begin
lib.vendor_lib = "lib/bundler/vendor/thor"
end
+ Automatiek::RakeTask.new("fileutils") do |lib|
+ lib.download = { :github => "https://github.com/ruby/fileutils" }
+ lib.namespace = "FileUtils"
+ lib.prefix = "Bundler"
+ lib.vendor_lib = "lib/bundler/vendor/fileutils"
+ end
+
Automatiek::RakeTask.new("net-http-persistent") do |lib|
lib.download = { :github => "https://github.com/drbrain/net-http-persistent" }
lib.namespace = "Net::HTTP::Persistent"
@@ -358,6 +365,7 @@ begin
end
rescue LoadError
namespace :vendor do
+ task(:fileutils) { abort "Install the automatiek gem to be able to vendor gems." }
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("net-http-persistent") { abort "Install the automatiek gem to be able to vendor gems." }
diff --git a/lib/bundler.rb b/lib/bundler.rb
index c9d8846ce5..b0f6869423 100644
--- a/lib/bundler.rb
+++ b/lib/bundler.rb
@@ -1,9 +1,8 @@
# frozen_string_literal: true
-require "fileutils"
+require "bundler/vendored_fileutils"
require "pathname"
require "rbconfig"
require "thread"
-require "tmpdir"
require "bundler/errors"
require "bundler/environment_preserver"
@@ -169,6 +168,7 @@ module Bundler
def tmp_home_path(login, warning)
login ||= "unknown"
+ Kernel.send(:require, "tmpdir")
path = Pathname.new(Dir.tmpdir).join("bundler", "home")
SharedHelpers.filesystem_access(path) do |tmp_home_path|
unless tmp_home_path.exist?
@@ -229,6 +229,7 @@ module Bundler
end
def tmp(name = Process.pid.to_s)
+ Kernel.send(:require, "tmpdir")
Pathname.new(Dir.mktmpdir(["bundler", name]))
end
diff --git a/lib/bundler/compact_index_client/updater.rb b/lib/bundler/compact_index_client/updater.rb
index 5aa480fdcc..adf013de04 100644
--- a/lib/bundler/compact_index_client/updater.rb
+++ b/lib/bundler/compact_index_client/updater.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
-require "fileutils"
+require "bundler/vendored_fileutils"
require "stringio"
-require "tmpdir"
require "zlib"
module Bundler
@@ -22,6 +21,7 @@ module Bundler
def initialize(fetcher)
@fetcher = fetcher
+ require "tmpdir"
end
def update(local_path, remote_path, retrying = nil)
diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb
index 68bc40844e..9a0ee5cf17 100644
--- a/lib/bundler/definition.rb
+++ b/lib/bundler/definition.rb
@@ -362,51 +362,8 @@ module Bundler
end
def to_lock
- out = String.new
-
- sources.lock_sources.each do |source|
- # Add the source header
- out << source.to_lock
- # Find all specs for this source
- resolve.
- select {|s| source.can_lock?(s) }.
- # This needs to be sorted by full name so that
- # gems with the same name, but different platform
- # are ordered consistently
- sort_by(&:full_name).
- each do |spec|
- next if spec.name == "bundler"
- out << spec.to_lock
- end
- out << "\n"
- end
-
- out << "PLATFORMS\n"
-
- platforms.map(&:to_s).sort.each do |p|
- out << " #{p}\n"
- end
-
- out << "\n"
- out << "DEPENDENCIES\n"
-
- handled = []
- dependencies.sort_by(&:to_s).each do |dep|
- next if handled.include?(dep.name)
- out << dep.to_lock
- handled << dep.name
- end
-
- if locked_ruby_version
- out << "\nRUBY VERSION\n"
- out << " #{locked_ruby_version}\n"
- end
-
- # Record the version of Bundler that was used to create the lockfile
- out << "\nBUNDLED WITH\n"
- out << " #{locked_bundler_version}\n"
-
- out
+ require "bundler/lockfile_generator"
+ LockfileGenerator.generate(self)
end
def ensure_equivalent_gemfile_and_lockfile(explicit_flag = false)
diff --git a/lib/bundler/feature_flag.rb b/lib/bundler/feature_flag.rb
index 180c442ffb..e901e047ed 100644
--- a/lib/bundler/feature_flag.rb
+++ b/lib/bundler/feature_flag.rb
@@ -39,6 +39,7 @@ module Bundler
settings_flag(:plugins) { @bundler_version >= Gem::Version.new("1.14") }
settings_flag(:prefer_gems_rb) { bundler_2_mode? }
settings_flag(:skip_default_git_sources) { bundler_2_mode? }
+ settings_flag(:suppress_install_using_messages) { bundler_2_mode? }
settings_flag(:unlock_source_unlocks_spec) { !bundler_2_mode? }
settings_flag(:update_requires_all_flag) { bundler_2_mode? }
diff --git a/lib/bundler/lockfile_generator.rb b/lib/bundler/lockfile_generator.rb
new file mode 100644
index 0000000000..2a29b20f79
--- /dev/null
+++ b/lib/bundler/lockfile_generator.rb
@@ -0,0 +1,94 @@
+# frozen_string_literal: true
+module Bundler
+ class LockfileGenerator
+ attr_reader :definition
+ attr_reader :out
+
+ # @private
+ def initialize(definition)
+ @definition = definition
+ @out = String.new
+ end
+
+ def self.generate(definition)
+ new(definition).generate!
+ end
+
+ def generate!
+ add_sources
+ add_platforms
+ add_dependencies
+ add_locked_ruby_version
+ add_bundled_with
+
+ out
+ end
+
+ private
+
+ def add_sources
+ definition.send(:sources).lock_sources.each_with_index do |source, idx|
+ out << "\n" unless idx.zero?
+
+ # Add the source header
+ out << source.to_lock
+
+ # Find all specs for this source
+ specs = definition.resolve.select {|s| source.can_lock?(s) }
+ add_specs(specs)
+ end
+ end
+
+ def add_specs(specs)
+ # This needs to be sorted by full name so that
+ # gems with the same name, but different platform
+ # are ordered consistently
+ specs.sort_by(&:full_name).each do |spec|
+ next if spec.name == "bundler".freeze
+ out << spec.to_lock
+ end
+ end
+
+ def add_platforms
+ add_section("PLATFORMS", definition.platforms)
+ end
+
+ def add_dependencies
+ out << "\nDEPENDENCIES\n"
+
+ handled = []
+ definition.dependencies.sort_by(&:to_s).each do |dep|
+ next if handled.include?(dep.name)
+ out << dep.to_lock
+ handled << dep.name
+ end
+ end
+
+ def add_locked_ruby_version
+ return unless locked_ruby_version = definition.locked_ruby_version
+ add_section("RUBY VERSION", locked_ruby_version.to_s)
+ end
+
+ def add_bundled_with
+ add_section("BUNDLED WITH", definition.locked_bundler_version.to_s)
+ end
+
+ def add_section(name, value)
+ out << "\n#{name}\n"
+ case value
+ when Array
+ value.map(&:to_s).sort.each do |val|
+ out << " #{val}\n"
+ end
+ when Hash
+ value.to_a.sort_by {|k, _| k.to_s }.each do |key, val|
+ out << " #{key}: #{val}\n"
+ end
+ when String
+ out << " #{value}\n"
+ else
+ raise ArgumentError, "#{value.inspect} can't be serialized in a lockfile"
+ end
+ end
+ end
+end
diff --git a/lib/bundler/settings.rb b/lib/bundler/settings.rb
index 8e9848d57a..afea575bf6 100644
--- a/lib/bundler/settings.rb
+++ b/lib/bundler/settings.rb
@@ -37,6 +37,7 @@ module Bundler
prefer_gems_rb
silence_root_warning
skip_default_git_sources
+ suppress_install_using_messages
unlock_source_unlocks_spec
update_requires_all_flag
].freeze
diff --git a/lib/bundler/source.rb b/lib/bundler/source.rb
index cb5f2da83e..f3d50adecf 100644
--- a/lib/bundler/source.rb
+++ b/lib/bundler/source.rb
@@ -60,5 +60,13 @@ module Bundler
def earlier_version?(spec_version, locked_spec_version)
Gem::Version.new(spec_version) < Gem::Version.new(locked_spec_version)
end
+
+ def print_using_message(message)
+ if !message.include?("(was ") && Bundler.feature_flag.suppress_install_using_messages?
+ Bundler.ui.debug message
+ else
+ Bundler.ui.info message
+ end
+ end
end
end
diff --git a/lib/bundler/source/git.rb b/lib/bundler/source/git.rb
index dc204a2b33..d1e32946eb 100644
--- a/lib/bundler/source/git.rb
+++ b/lib/bundler/source/git.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require "fileutils"
+require "bundler/vendored_fileutils"
require "uri"
require "digest/sha1"
@@ -169,7 +169,7 @@ module Bundler
def install(spec, options = {})
force = options[:force]
- Bundler.ui.info "Using #{version_message(spec)} from #{self}"
+ print_using_message "Using #{version_message(spec)} from #{self}"
if requires_checkout? && !@copied && !force
Bundler.ui.debug " * Checking out revision: #{ref}"
diff --git a/lib/bundler/source/metadata.rb b/lib/bundler/source/metadata.rb
index f6d2f0729d..1d62a1f76a 100644
--- a/lib/bundler/source/metadata.rb
+++ b/lib/bundler/source/metadata.rb
@@ -36,7 +36,7 @@ module Bundler
end
def install(spec, _opts = {})
- Bundler.ui.info "Using #{version_message(spec)}"
+ print_using_message "Using #{version_message(spec)}"
nil
end
diff --git a/lib/bundler/source/path.rb b/lib/bundler/source/path.rb
index 99808d2ef2..84a98420a9 100644
--- a/lib/bundler/source/path.rb
+++ b/lib/bundler/source/path.rb
@@ -76,7 +76,7 @@ module Bundler
end
def install(spec, options = {})
- Bundler.ui.info "Using #{version_message(spec)} from #{self}"
+ print_using_message "Using #{version_message(spec)} from #{self}"
generate_bin(spec, :disable_extensions => true)
nil # no post-install message
end
diff --git a/lib/bundler/source/rubygems.rb b/lib/bundler/source/rubygems.rb
index 70f7819f3a..e76a00361a 100644
--- a/lib/bundler/source/rubygems.rb
+++ b/lib/bundler/source/rubygems.rb
@@ -106,7 +106,7 @@ module Bundler
end
if installed?(spec) && !force
- Bundler.ui.info "Using #{version_message(spec)}"
+ print_using_message "Using #{version_message(spec)}"
return nil # no post-install message
end
diff --git a/lib/bundler/ssl_certs/certificate_manager.rb b/lib/bundler/ssl_certs/certificate_manager.rb
index a5e5d84b64..05de6410be 100644
--- a/lib/bundler/ssl_certs/certificate_manager.rb
+++ b/lib/bundler/ssl_certs/certificate_manager.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require "fileutils"
+require "bundler/vendored_fileutils"
require "net/https"
require "openssl"
diff --git a/lib/bundler/vendor/fileutils/lib/fileutils.rb b/lib/bundler/vendor/fileutils/lib/fileutils.rb
new file mode 100644
index 0000000000..cc69740845
--- /dev/null
+++ b/lib/bundler/vendor/fileutils/lib/fileutils.rb
@@ -0,0 +1,1638 @@
+# frozen_string_literal: true
+#
+# = fileutils.rb
+#
+# Copyright (c) 2000-2007 Minero Aoki
+#
+# This program is free software.
+# You can distribute/modify this program under the same terms of ruby.
+#
+# == module Bundler::FileUtils
+#
+# Namespace for several file utility methods for copying, moving, removing, etc.
+#
+# === Module Functions
+#
+# require 'bundler/vendor/fileutils/lib/fileutils'
+#
+# Bundler::FileUtils.cd(dir, options)
+# Bundler::FileUtils.cd(dir, options) {|dir| block }
+# Bundler::FileUtils.pwd()
+# Bundler::FileUtils.mkdir(dir, options)
+# Bundler::FileUtils.mkdir(list, options)
+# Bundler::FileUtils.mkdir_p(dir, options)
+# Bundler::FileUtils.mkdir_p(list, options)
+# Bundler::FileUtils.rmdir(dir, options)
+# Bundler::FileUtils.rmdir(list, options)
+# Bundler::FileUtils.ln(target, link, options)
+# Bundler::FileUtils.ln(targets, dir, options)
+# Bundler::FileUtils.ln_s(target, link, options)
+# Bundler::FileUtils.ln_s(targets, dir, options)
+# Bundler::FileUtils.ln_sf(target, link, options)
+# Bundler::FileUtils.cp(src, dest, options)
+# Bundler::FileUtils.cp(list, dir, options)
+# Bundler::FileUtils.cp_r(src, dest, options)
+# Bundler::FileUtils.cp_r(list, dir, options)
+# Bundler::FileUtils.mv(src, dest, options)
+# Bundler::FileUtils.mv(list, dir, options)
+# Bundler::FileUtils.rm(list, options)
+# Bundler::FileUtils.rm_r(list, options)
+# Bundler::FileUtils.rm_rf(list, options)
+# Bundler::FileUtils.install(src, dest, options)
+# Bundler::FileUtils.chmod(mode, list, options)
+# Bundler::FileUtils.chmod_R(mode, list, options)
+# Bundler::FileUtils.chown(user, group, list, options)
+# Bundler::FileUtils.chown_R(user, group, list, options)
+# Bundler::FileUtils.touch(list, options)
+#
+# The <tt>options</tt> parameter is a hash of options, taken from the list
+# <tt>:force</tt>, <tt>:noop</tt>, <tt>:preserve</tt>, and <tt>:verbose</tt>.
+# <tt>:noop</tt> means that no changes are made. The other three are obvious.
+# Each method documents the options that it honours.
+#
+# All methods that have the concept of a "source" file or directory can take
+# either one file or a list of files in that argument. See the method
+# documentation for examples.
+#
+# There are some `low level' methods, which do not accept any option:
+#
+# Bundler::FileUtils.copy_entry(src, dest, preserve = false, dereference = false)
+# Bundler::FileUtils.copy_file(src, dest, preserve = false, dereference = true)
+# Bundler::FileUtils.copy_stream(srcstream, deststream)
+# Bundler::FileUtils.remove_entry(path, force = false)
+# Bundler::FileUtils.remove_entry_secure(path, force = false)
+# Bundler::FileUtils.remove_file(path, force = false)
+# Bundler::FileUtils.compare_file(path_a, path_b)
+# Bundler::FileUtils.compare_stream(stream_a, stream_b)
+# Bundler::FileUtils.uptodate?(file, cmp_list)
+#
+# == module Bundler::FileUtils::Verbose
+#
+# This module has all methods of Bundler::FileUtils module, but it outputs messages
+# before acting. This equates to passing the <tt>:verbose</tt> flag to methods
+# in Bundler::FileUtils.
+#
+# == module Bundler::FileUtils::NoWrite
+#
+# This module has all methods of Bundler::FileUtils module, but never changes
+# files/directories. This equates to passing the <tt>:noop</tt> flag to methods
+# in Bundler::FileUtils.
+#
+# == module Bundler::FileUtils::DryRun
+#
+# This module has all methods of Bundler::FileUtils module, but never changes
+# files/directories. This equates to passing the <tt>:noop</tt> and
+# <tt>:verbose</tt> flags to methods in Bundler::FileUtils.
+#
+
+module Bundler::FileUtils
+
+ def self.private_module_function(name) #:nodoc:
+ module_function name
+ private_class_method name
+ end
+
+ #
+ # Returns the name of the current directory.
+ #
+ def pwd
+ Dir.pwd
+ end
+ module_function :pwd
+
+ alias getwd pwd
+ module_function :getwd
+
+ #
+ # Changes the current directory to the directory +dir+.
+ #
+ # If this method is called with block, resumes to the old
+ # working directory after the block execution finished.
+ #
+ # Bundler::FileUtils.cd('/', :verbose => true) # chdir and report it
+ #
+ # Bundler::FileUtils.cd('/') do # chdir
+ # # ... # do something
+ # end # return to original directory
+ #
+ def cd(dir, verbose: nil, &block) # :yield: dir
+ fu_output_message "cd #{dir}" if verbose
+ Dir.chdir(dir, &block)
+ fu_output_message 'cd -' if verbose and block
+ end
+ module_function :cd
+
+ alias chdir cd
+ module_function :chdir
+
+ #
+ # Returns true if +new+ is newer than all +old_list+.
+ # Non-existent files are older than any file.
+ #
+ # Bundler::FileUtils.uptodate?('hello.o', %w(hello.c hello.h)) or \
+ # system 'make hello.o'
+ #
+ def uptodate?(new, old_list)
+ return false unless File.exist?(new)
+ new_time = File.mtime(new)
+ old_list.each do |old|
+ if File.exist?(old)
+ return false unless new_time > File.mtime(old)
+ end
+ end
+ true
+ end
+ module_function :uptodate?
+
+ def remove_trailing_slash(dir) #:nodoc:
+ dir == '/' ? dir : dir.chomp(?/)
+ end
+ private_module_function :remove_trailing_slash
+
+ #
+ # Creates one or more directories.
+ #
+ # Bundler::FileUtils.mkdir 'test'
+ # Bundler::FileUtils.mkdir %w( tmp data )
+ # Bundler::FileUtils.mkdir 'notexist', :noop => true # Does not really create.
+ # Bundler::FileUtils.mkdir 'tmp', :mode => 0700
+ #
+ def mkdir(list, mode: nil, noop: nil, verbose: nil)
+ list = fu_list(list)
+ fu_output_message "mkdir #{mode ? ('-m %03o ' % mode) : ''}#{list.join ' '}" if verbose
+ return if noop
+
+ list.each do |dir|
+ fu_mkdir dir, mode
+ end
+ end
+ module_function :mkdir
+
+ #
+ # Creates a directory and all its parent directories.
+ # For example,
+ #
+ # Bundler::FileUtils.mkdir_p '/usr/local/lib/ruby'
+ #
+ # causes to make following directories, if it does not exist.
+ #
+ # * /usr
+ # * /usr/local
+ # * /usr/local/lib
+ # * /usr/local/lib/ruby
+ #
+ # You can pass several directories at a time in a list.
+ #
+ def mkdir_p(list, mode: nil, noop: nil, verbose: nil)
+ list = fu_list(list)
+ fu_output_message "mkdir -p #{mode ? ('-m %03o ' % mode) : ''}#{list.join ' '}" if verbose
+ return *list if noop
+
+ list.map {|path| remove_trailing_slash(path)}.each do |path|
+ # optimize for the most common case
+ begin
+ fu_mkdir path, mode
+ next
+ rescue SystemCallError
+ next if File.directory?(path)
+ end
+
+ stack = []
+ until path == stack.last # dirname("/")=="/", dirname("C:/")=="C:/"
+ stack.push path
+ path = File.dirname(path)
+ end
+ stack.pop # root directory should exist
+ stack.reverse_each do |dir|
+ begin
+ fu_mkdir dir, mode
+ rescue SystemCallError
+ raise unless File.directory?(dir)
+ end
+ end
+ end
+
+ return *list
+ end
+ module_function :mkdir_p
+
+ alias mkpath mkdir_p
+ alias makedirs mkdir_p
+ module_function :mkpath
+ module_function :makedirs
+
+ def fu_mkdir(path, mode) #:nodoc:
+ path = remove_trailing_slash(path)
+ if mode
+ Dir.mkdir path, mode
+ File.chmod mode, path
+ else
+ Dir.mkdir path
+ end
+ end
+ private_module_function :fu_mkdir
+
+ #
+ # Removes one or more directories.
+ #
+ # Bundler::FileUtils.rmdir 'somedir'
+ # Bundler::FileUtils.rmdir %w(somedir anydir otherdir)
+ # # Does not really remove directory; outputs message.
+ # Bundler::FileUtils.rmdir 'somedir', :verbose => true, :noop => true
+ #
+ def rmdir(list, parents: nil, noop: nil, verbose: nil)
+ list = fu_list(list)
+ fu_output_message "rmdir #{parents ? '-p ' : ''}#{list.join ' '}" if verbose
+ return if noop
+ list.each do |dir|
+ begin
+ Dir.rmdir(dir = remove_trailing_slash(dir))
+ if parents
+ until (parent = File.dirname(dir)) == '.' or parent == dir
+ dir = parent
+ Dir.rmdir(dir)
+ end
+ end
+ rescue Errno::ENOTEMPTY, Errno::EEXIST, Errno::ENOENT
+ end
+ end
+ end
+ module_function :rmdir
+
+ #
+ # :call-seq:
+ # Bundler::FileUtils.ln(target, link, force: nil, noop: nil, verbose: nil)
+ # Bundler::FileUtils.ln(target, dir, force: nil, noop: nil, verbose: nil)
+ # Bundler::FileUtils.ln(targets, dir, force: nil, noop: nil, verbose: nil)
+ #
+ # In the first form, creates a hard link +link+ which points to +target+.
+ # If +link+ already exists, raises Errno::EEXIST.
+ # But if the :force option is set, overwrites +link+.
+ #
+ # Bundler::FileUtils.ln 'gcc', 'cc', verbose: true
+ # Bundler::FileUtils.ln '/usr/bin/emacs21', '/usr/bin/emacs'
+ #
+ # In the second form, creates a link +dir/target+ pointing to +target+.
+ # In the third form, creates several hard links in the directory +dir+,
+ # pointing to each item in +targets+.
+ # If +dir+ is not a directory, raises Errno::ENOTDIR.
+ #
+ # Bundler::FileUtils.cd '/sbin'
+ # Bundler::FileUtils.ln %w(cp mv mkdir), '/bin' # Now /sbin/cp and /bin/cp are linked.
+ #
+ def ln(src, dest, force: nil, noop: nil, verbose: nil)
+ fu_output_message "ln#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose
+ return if noop
+ fu_each_src_dest0(src, dest) do |s,d|
+ remove_file d, true if force
+ File.link s, d
+ end
+ end
+ module_function :ln
+
+ alias link ln
+ module_function :link
+
+ #
+ # :call-seq:
+ # Bundler::FileUtils.ln_s(target, link, force: nil, noop: nil, verbose: nil)
+ # Bundler::FileUtils.ln_s(target, dir, force: nil, noop: nil, verbose: nil)
+ # Bundler::FileUtils.ln_s(targets, dir, force: nil, noop: nil, verbose: nil)
+ #
+ # In the first form, creates a symbolic link +link+ which points to +target+.
+ # If +link+ already exists, raises Errno::EEXIST.
+ # But if the :force option is set, overwrites +link+.
+ #
+ # Bundler::FileUtils.ln_s '/usr/bin/ruby', '/usr/local/bin/ruby'
+ # Bundler::FileUtils.ln_s 'verylongsourcefilename.c', 'c', force: true
+ #
+ # In the second form, creates a link +dir/target+ pointing to +target+.
+ # In the third form, creates several symbolic links in the directory +dir+,
+ # pointing to each item in +targets+.
+ # If +dir+ is not a directory, raises Errno::ENOTDIR.
+ #
+ # Bundler::FileUtils.ln_s Dir.glob('/bin/*.rb'), '/home/foo/bin'
+ #
+ def ln_s(src, dest, force: nil, noop: nil, verbose: nil)
+ fu_output_message "ln -s#{force ? 'f' : ''} #{[src,dest].flatten.join ' '}" if verbose
+ return if noop
+ fu_each_src_dest0(src, dest) do |s,d|
+ remove_file d, true if force
+ File.symlink s, d
+ end
+ end
+ module_function :ln_s
+
+ alias symlink ln_s
+ module_function :symlink
+
+ #
+ # :call-seq:
+ # Bundler::FileUtils.ln_sf(*args)
+ #
+ # Same as
+ #
+ # Bundler::FileUtils.ln_s(*args, force: true)
+ #
+ def ln_sf(src, dest, noop: nil, verbose: nil)
+ ln_s src, dest, force: true, noop: noop, verbose: verbose
+ end
+ module_function :ln_sf
+
+ #
+ # Copies a file content +src+ to +dest+. If +dest+ is a directory,
+ # copies +src+ to +dest/src+.
+ #
+ # If +src+ is a list of files, then +dest+ must be a directory.
+ #
+ # Bundler::FileUtils.cp 'eval.c', 'eval.c.org'
+ # Bundler::FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6'
+ # Bundler::FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6', :verbose => true
+ # Bundler::FileUtils.cp 'symlink', 'dest' # copy content, "dest" is not a symlink
+ #
+ def cp(src, dest, preserve: nil, noop: nil, verbose: nil)
+ fu_output_message "cp#{preserve ? ' -p' : ''} #{[src,dest].flatten.join ' '}" if verbose
+ return if noop
+ fu_each_src_dest(src, dest) do |s, d|
+ copy_file s, d, preserve
+ end
+ end
+ module_function :cp
+
+ alias copy cp
+ module_function :copy
+
+ #
+ # Copies +src+ to +dest+. If +src+ is a directory, this method copies
+ # all its contents recursively. If +dest+ is a directory, copies
+ # +src+ to +dest/src+.
+ #
+ # +src+ can be a list of files.
+ #
+ # # Installing Ruby library "mylib" under the site_ruby
+ # Bundler::FileUtils.rm_r site_ruby + '/mylib', :force
+ # Bundler::FileUtils.cp_r 'lib/', site_ruby + '/mylib'
+ #
+ # # Examples of copying several files to target directory.
+ # Bundler::FileUtils.cp_r %w(mail.rb field.rb debug/), site_ruby + '/tmail'
+ # Bundler::FileUtils.cp_r Dir.glob('*.rb'), '/home/foo/lib/ruby', :noop => true, :verbose => true
+ #
+ # # If you want to copy all contents of a directory instead of the
+ # # directory itself, c.f. src/x -> dest/x, src/y -> dest/y,
+ # # use following code.
+ # Bundler::FileUtils.cp_r 'src/.', 'dest' # cp_r('src', 'dest') makes dest/src,
+ # # but this doesn't.
+ #
+ def cp_r(src, dest, preserve: nil, noop: nil, verbose: nil,
+ dereference_root: true, remove_destination: nil)
+ fu_output_message "cp -r#{preserve ? 'p' : ''}#{remove_destination ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}" if verbose
+ return if noop
+ fu_each_src_dest(src, dest) do |s, d|
+ copy_entry s, d, preserve, dereference_root, remove_destination
+ end
+ end
+ module_function :cp_r
+
+ #
+ # Copies a file system entry +src+ to +dest+.
+ # If +src+ is a directory, this method copies its contents recursively.
+ # This method preserves file types, c.f. symlink, directory...
+ # (FIFO, device files and etc. are not supported yet)
+ #
+ # Both of +src+ and +dest+ must be a path name.
+ # +src+ must exist, +dest+ must not exist.
+ #
+ # If +preserve+ is true, this method preserves owner, group, and
+ # modified time. Permissions are copied regardless +preserve+.
+ #
+ # If +dereference_root+ is true, this method dereference tree root.
+ #
+ # If +remove_destination+ is true, this method removes each destination file before copy.
+ #
+ def copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false)
+ Entry_.new(src, nil, dereference_root).wrap_traverse(proc do |ent|
+ destent = Entry_.new(dest, ent.rel, false)
+ File.unlink destent.path if remove_destination && File.file?(destent.path)
+ ent.copy destent.path
+ end, proc do |ent|
+ destent = Entry_.new(dest, ent.rel, false)
+ ent.copy_metadata destent.path if preserve
+ end)
+ end
+ module_function :copy_entry
+
+ #
+ # Copies file contents of +src+ to +dest+.
+ # Both of +src+ and +dest+ must be a path name.
+ #
+ def copy_file(src, dest, preserve = false, dereference = true)
+ ent = Entry_.new(src, nil, dereference)
+ ent.copy_file dest
+ ent.copy_metadata dest if preserve
+ end
+ module_function :copy_file
+
+ #
+ # Copies stream +src+ to +dest+.
+ # +src+ must respond to #read(n) and
+ # +dest+ must respond to #write(str).
+ #
+ def copy_stream(src, dest)
+ IO.copy_stream(src, dest)
+ end
+ module_function :copy_stream
+
+ #
+ # Moves file(s) +src+ to +dest+. If +file+ and +dest+ exist on the different
+ # disk partition, the file is copied then the original file is removed.
+ #
+ # Bundler::FileUtils.mv 'badname.rb', 'goodname.rb'
+ # Bundler::FileUtils.mv 'stuff.rb', '/notexist/lib/ruby', :force => true # no error
+ #
+ # Bundler::FileUtils.mv %w(junk.txt dust.txt), '/home/foo/.trash/'
+ # Bundler::FileUtils.mv Dir.glob('test*.rb'), 'test', :noop => true, :verbose => true
+ #
+ def mv(src, dest, force: nil, noop: nil, verbose: nil, secure: nil)
+ fu_output_message "mv#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose
+ return if noop
+ fu_each_src_dest(src, dest) do |s, d|
+ destent = Entry_.new(d, nil, true)
+ begin
+ if destent.exist?
+ if destent.directory?
+ raise Errno::EEXIST, d
+ else
+ destent.remove_file if rename_cannot_overwrite_file?
+ end
+ end
+ begin
+ File.rename s, d
+ rescue Errno::EXDEV
+ copy_entry s, d, true
+ if secure
+ remove_entry_secure s, force
+ else
+ remove_entry s, force
+ end
+ end
+ rescue SystemCallError
+ raise unless force
+ end
+ end
+ end
+ module_function :mv
+
+ alias move mv
+ module_function :move
+
+ def rename_cannot_overwrite_file? #:nodoc:
+ /emx/ =~ RUBY_PLATFORM
+ end
+ private_module_function :rename_cannot_overwrite_file?
+
+ #
+ # Remove file(s) specified in +list+. This method cannot remove directories.
+ # All StandardErrors are ignored when the :force option is set.
+ #
+ # Bundler::FileUtils.rm %w( junk.txt dust.txt )
+ # Bundler::FileUtils.rm Dir.glob('*.so')
+ # Bundler::FileUtils.rm 'NotExistFile', :force => true # never raises exception
+ #
+ def rm(list, force: nil, noop: nil, verbose: nil)
+ list = fu_list(list)
+ fu_output_message "rm#{force ? ' -f' : ''} #{list.join ' '}" if verbose
+ return if noop
+
+ list.each do |path|
+ remove_file path, force
+ end
+ end
+ module_function :rm
+
+ alias remove rm
+ module_function :remove
+
+ #
+ # Equivalent to
+ #
+ # Bundler::FileUtils.rm(list, :force => true)
+ #
+ def rm_f(list, noop: nil, verbose: nil)
+ rm list, force: true, noop: noop, verbose: verbose
+ end
+ module_function :rm_f
+
+ alias safe_unlink rm_f
+ module_function :safe_unlink
+
+ #
+ # remove files +list+[0] +list+[1]... If +list+[n] is a directory,
+ # removes its all contents recursively. This method ignores
+ # StandardError when :force option is set.
+ #
+ # Bundler::FileUtils.rm_r Dir.glob('/tmp/*')
+ # Bundler::FileUtils.rm_r 'some_dir', :force => true
+ #
+ # WARNING: This method causes local vulnerability
+ # if one of parent directories or removing directory tree are world
+ # writable (including /tmp, whose permission is 1777), and the current
+ # process has strong privilege such as Unix super user (root), and the
+ # system has symbolic link. For secure removing, read the documentation
+ # of #remove_entry_secure carefully, and set :secure option to true.
+ # Default is :secure=>false.
+ #
+ # NOTE: This method calls #remove_entry_secure if :secure option is set.
+ # See also #remove_entry_secure.
+ #
+ def rm_r(list, force: nil, noop: nil, verbose: nil, secure: nil)
+ list = fu_list(list)
+ fu_output_message "rm -r#{force ? 'f' : ''} #{list.join ' '}" if verbose
+ return if noop
+ list.each do |path|
+ if secure
+ remove_entry_secure path, force
+ else
+ remove_entry path, force
+ end
+ end
+ end
+ module_function :rm_r
+
+ #
+ # Equivalent to
+ #
+ # Bundler::FileUtils.rm_r(list, :force => true)
+ #
+ # WARNING: This method causes local vulnerability.
+ # Read the documentation of #rm_r first.
+ #
+ def rm_rf(list, noop: nil, verbose: nil, secure: nil)
+ rm_r list, force: true, noop: noop, verbose: verbose, secure: secure
+ end
+ module_function :rm_rf
+
+ alias rmtree rm_rf
+ module_function :rmtree
+
+ #
+ # This method removes a file system entry +path+. +path+ shall be a
+ # regular file, a directory, or something. If +path+ is a directory,
+ # remove it recursively. This method is required to avoid TOCTTOU
+ # (time-of-check-to-time-of-use) local security vulnerability of #rm_r.
+ # #rm_r causes security hole when:
+ #
+ # * Parent directory is world writable (including /tmp).
+ # * Removing directory tree includes world writable directory.
+ # * The system has symbolic link.
+ #
+ # To avoid this security hole, this method applies special preprocess.
+ # If +path+ is a directory, this method chown(2) and chmod(2) all
+ # removing directories. This requires the current process is the
+ # owner of the removing whole directory tree, or is the super user (root).
+ #
+ # WARNING: You must ensure that *ALL* parent directories cannot be
+ # moved by other untrusted users. For example, parent directories
+ # should not be owned by untrusted users, and should not be world
+ # writable except when the sticky bit set.
+ #
+ # WARNING: Only the owner of the removing directory tree, or Unix super
+ # user (root) should invoke this method. Otherwise this method does not
+ # work.
+ #
+ # For details of this security vulnerability, see Perl's case:
+ #
+ # * http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2005-0448
+ # * http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0452
+ #
+ # For fileutils.rb, this vulnerability is reported in [ruby-dev:26100].
+ #
+ def remove_entry_secure(path, force = false)
+ unless fu_have_symlink?
+ remove_entry path, force
+ return
+ end
+ fullpath = File.expand_path(path)
+ st = File.lstat(fullpath)
+ unless st.directory?
+ File.unlink fullpath
+ return
+ end
+ # is a directory.
+ parent_st = File.stat(File.dirname(fullpath))
+ unless parent_st.world_writable?
+ remove_entry path, force
+ return
+ end
+ unless parent_st.sticky?
+ raise ArgumentError, "parent directory is world writable, Bundler::FileUtils#remove_entry_secure does not work; abort: #{path.inspect} (parent directory mode #{'%o' % parent_st.mode})"
+ end
+ # freeze tree root
+ euid = Process.euid
+ File.open(fullpath + '/.') {|f|
+ unless fu_stat_identical_entry?(st, f.stat)
+ # symlink (TOC-to-TOU attack?)
+ File.unlink fullpath
+ return
+ end
+ f.chown euid, -1
+ f.chmod 0700
+ unless fu_stat_identical_entry?(st, File.lstat(fullpath))
+ # TOC-to-TOU attack?
+ File.unlink fullpath
+ return
+ end
+ }
+ # ---- tree root is frozen ----
+ root = Entry_.new(path)
+ root.preorder_traverse do |ent|
+ if ent.directory?
+ ent.chown euid, -1
+ ent.chmod 0700
+ end
+ end
+ root.postorder_traverse do |ent|
+ begin
+ ent.remove
+ rescue
+ raise unless force
+ end
+ end
+ rescue
+ raise unless force
+ end
+ module_function :remove_entry_secure
+
+ def fu_have_symlink? #:nodoc:
+ File.symlink nil, nil
+ rescue NotImplementedError
+ return false
+ rescue TypeError
+ return true
+ end
+ private_module_function :fu_have_symlink?
+
+ def fu_stat_identical_entry?(a, b) #:nodoc:
+ a.dev == b.dev and a.ino == b.ino
+ end
+ private_module_function :fu_stat_identical_entry?
+
+ #
+ # This method removes a file system entry +path+.
+ # +path+ might be a regular file, a directory, or something.
+ # If +path+ is a directory, remove it recursively.
+ #
+ # See also #remove_entry_secure.
+ #
+ def remove_entry(path, force = false)
+ Entry_.new(path).postorder_traverse do |ent|
+ begin
+ ent.remove
+ rescue
+ raise unless force
+ end
+ end
+ rescue
+ raise unless force
+ end
+ module_function :remove_entry
+
+ #
+ # Removes a file +path+.
+ # This method ignores StandardError if +force+ is true.
+ #
+ def remove_file(path, force = false)
+ Entry_.new(path).remove_file
+ rescue
+ raise unless force
+ end
+ module_function :remove_file
+
+ #
+ # Removes a directory +dir+ and its contents recursively.
+ # This method ignores StandardError if +force+ is true.
+ #
+ def remove_dir(path, force = false)
+ remove_entry path, force # FIXME?? check if it is a directory
+ end
+ module_function :remove_dir
+
+ #
+ # Returns true if the contents of a file +a+ and a file +b+ are identical.
+ #
+ # Bundler::FileUtils.compare_file('somefile', 'somefile') #=> true
+ # Bundler::FileUtils.compare_file('/dev/null', '/dev/urandom') #=> false
+ #
+ def compare_file(a, b)
+ return false unless File.size(a) == File.size(b)
+ File.open(a, 'rb') {|fa|
+ File.open(b, 'rb') {|fb|
+ return compare_stream(fa, fb)
+ }
+ }
+ end
+ module_function :compare_file
+
+ alias identical? compare_file
+ alias cmp compare_file
+ module_function :identical?
+ module_function :cmp
+
+ #
+ # Returns true if the contents of a stream +a+ and +b+ are identical.
+ #
+ def compare_stream(a, b)
+ bsize = fu_stream_blksize(a, b)
+ sa = String.new(capacity: bsize)
+ sb = String.new(capacity: bsize)
+ begin
+ a.read(bsize, sa)
+ b.read(bsize, sb)
+ return true if sa.empty? && sb.empty?
+ end while sa == sb
+ false
+ end
+ module_function :compare_stream
+
+ #
+ # If +src+ is not same as +dest+, copies it and changes the permission
+ # mode to +mode+. If +dest+ is a directory, destination is +dest+/+src+.
+ # This method removes destination before copy.
+ #
+ # Bundler::FileUtils.install 'ruby', '/usr/local/bin/ruby', :mode => 0755, :verbose => true
+ # Bundler::FileUtils.install 'lib.rb', '/usr/local/lib/ruby/site_ruby', :verbose => true
+ #
+ def install(src, dest, mode: nil, owner: nil, group: nil, preserve: nil,
+ noop: nil, verbose: nil)
+ if verbose
+ msg = +"install -c"
+ msg << ' -p' if preserve
+ msg << ' -m ' << mode_to_s(mode) if mode
+ msg << " -o #{owner}" if owner
+ msg << " -g #{group}" if group
+ msg << ' ' << [src,dest].flatten.join(' ')
+ fu_output_message msg
+ end
+ return if noop
+ uid = fu_get_uid(owner)
+ gid = fu_get_gid(group)
+ fu_each_src_dest(src, dest) do |s, d|
+ st = File.stat(s)
+ unless File.exist?(d) and compare_file(s, d)
+ remove_file d, true
+ copy_file s, d
+ File.utime st.atime, st.mtime, d if preserve
+ File.chmod fu_mode(mode, st), d if mode
+ File.chown uid, gid, d if uid or gid
+ end
+ end
+ end
+ module_function :install
+
+ def user_mask(target) #:nodoc:
+ target.each_char.inject(0) do |mask, chr|
+ case chr
+ when "u"
+ mask | 04700
+ when "g"
+ mask | 02070
+ when "o"
+ mask | 01007
+ when "a"
+ mask | 07777
+ else
+ raise ArgumentError, "invalid `who' symbol in file mode: #{chr}"
+ end
+ end
+ end
+ private_module_function :user_mask
+
+ def apply_mask(mode, user_mask, op, mode_mask) #:nodoc:
+ case op
+ when '='
+ (mode & ~user_mask) | (user_mask & mode_mask)
+ when '+'
+ mode | (user_mask & mode_mask)
+ when '-'
+ mode & ~(user_mask & mode_mask)
+ end
+ end
+ private_module_function :apply_mask
+
+ def symbolic_modes_to_i(mode_sym, path) #:nodoc:
+ mode = if File::Stat === path
+ path.mode
+ else
+ File.stat(path).mode
+ end
+ mode_sym.split(/,/).inject(mode & 07777) do |current_mode, clause|
+ target, *actions = clause.split(/([=+-])/)
+ raise ArgumentError, "invalid file mode: #{mode_sym}" if actions.empty?
+ target = 'a' if target.empty?
+ user_mask = user_mask(target)
+ actions.each_slice(2) do |op, perm|
+ need_apply = op == '='
+ mode_mask = (perm || '').each_char.inject(0) do |mask, chr|
+ case chr
+ when "r"
+ mask | 0444
+ when "w"
+ mask | 0222
+ when "x"
+ mask | 0111
+ when "X"
+ if FileTest.directory? path
+ mask | 0111
+ else
+ mask
+ end
+ when "s"
+ mask | 06000
+ when "t"
+ mask | 01000
+ when "u", "g", "o"
+ if mask.nonzero?
+ current_mode = apply_mask(current_mode, user_mask, op, mask)
+ end
+ need_apply = false
+ copy_mask = user_mask(chr)
+ (current_mode & copy_mask) / (copy_mask & 0111) * (user_mask & 0111)
+ else
+ raise ArgumentError, "invalid `perm' symbol in file mode: #{chr}"
+ end
+ end
+
+ if mode_mask.nonzero? || need_apply
+ current_mode = apply_mask(current_mode, user_mask, op, mode_mask)
+ end
+ end
+ current_mode
+ end
+ end
+ private_module_function :symbolic_modes_to_i
+
+ def fu_mode(mode, path) #:nodoc:
+ mode.is_a?(String) ? symbolic_modes_to_i(mode, path) : mode
+ end
+ private_module_function :fu_mode
+
+ def mode_to_s(mode) #:nodoc:
+ mode.is_a?(String) ? mode : "%o" % mode
+ end
+ private_module_function :mode_to_s
+
+ #
+ # Changes permission bits on the named files (in +list+) to the bit pattern
+ # represented by +mode+.
+ #
+ # +mode+ is the symbolic and absolute mode can be used.
+ #
+ # Absolute mode is
+ # Bundler::FileUtils.chmod 0755, 'somecommand'
+ # Bundler::FileUtils.chmod 0644, %w(my.rb your.rb his.rb her.rb)
+ # Bundler::FileUtils.chmod 0755, '/usr/bin/ruby', :verbose => true
+ #
+ # Symbolic mode is
+ # Bundler::FileUtils.chmod "u=wrx,go=rx", 'somecommand'
+ # Bundler::FileUtils.chmod "u=wr,go=rr", %w(my.rb your.rb his.rb her.rb)
+ # Bundler::FileUtils.chmod "u=wrx,go=rx", '/usr/bin/ruby', :verbose => true
+ #
+ # "a" :: is user, group, other mask.
+ # "u" :: is user's mask.
+ # "g" :: is group's mask.
+ # "o" :: is other's mask.
+ # "w" :: is write permission.
+ # "r" :: is read permission.
+ # "x" :: is execute permission.
+ # "X" ::
+ # is execute permission for directories only, must be used in conjunction with "+"
+ # "s" :: is uid, gid.
+ # "t" :: is sticky bit.
+ # "+" :: is added to a class given the specified mode.
+ # "-" :: Is removed from a given class given mode.
+ # "=" :: Is the exact nature of the class will be given a specified mode.
+
+ def chmod(mode, list, noop: nil, verbose: nil)
+ list = fu_list(list)
+ fu_output_message sprintf('chmod %s %s', mode_to_s(mode), list.join(' ')) if verbose
+ return if noop
+ list.each do |path|
+ Entry_.new(path).chmod(fu_mode(mode, path))
+ end
+ end
+ module_function :chmod
+
+ #
+ # Changes permission bits on the named files (in +list+)
+ # to the bit pattern represented by +mode+.
+ #
+ # Bundler::FileUtils.chmod_R 0700, "/tmp/app.#{$$}"
+ # Bundler::FileUtils.chmod_R "u=wrx", "/tmp/app.#{$$}"
+ #
+ def chmod_R(mode, list, noop: nil, verbose: nil, force: nil)
+ list = fu_list(list)
+ fu_output_message sprintf('chmod -R%s %s %s',
+ (force ? 'f' : ''),
+ mode_to_s(mode), list.join(' ')) if verbose
+ return if noop
+ list.each do |root|
+ Entry_.new(root).traverse do |ent|
+ begin
+ ent.chmod(fu_mode(mode, ent.path))
+ rescue
+ raise unless force
+ end
+ end
+ end
+ end
+ module_function :chmod_R
+
+ #
+ # Changes owner and group on the named files (in +list+)
+ # to the user +user+ and the group +group+. +user+ and +group+
+ # may be an ID (Integer/String) or a name (String).
+ # If +user+ or +group+ is nil, this method does not change
+ # the attribute.
+ #
+ # Bundler::FileUtils.chown 'root', 'staff', '/usr/local/bin/ruby'
+ # Bundler::FileUtils.chown nil, 'bin', Dir.glob('/usr/bin/*'), :verbose => true
+ #
+ def chown(user, group, list, noop: nil, verbose: nil)
+ list = fu_list(list)
+ fu_output_message sprintf('chown %s %s',
+ (group ? "#{user}:#{group}" : user || ':'),
+ list.join(' ')) if verbose
+ return if noop
+ uid = fu_get_uid(user)
+ gid = fu_get_gid(group)
+ list.each do |path|
+ Entry_.new(path).chown uid, gid
+ end
+ end
+ module_function :chown
+
+ #
+ # Changes owner and group on the named files (in +list+)
+ # to the user +user+ and the group +group+ recursively.
+ # +user+ and +group+ may be an ID (Integer/String) or
+ # a name (String). If +user+ or +group+ is nil, this
+ # method does not change the attribute.
+ #
+ # Bundler::FileUtils.chown_R 'www', 'www', '/var/www/htdocs'
+ # Bundler::FileUtils.chown_R 'cvs', 'cvs', '/var/cvs', :verbose => true
+ #
+ def chown_R(user, group, list, noop: nil, verbose: nil, force: nil)
+ list = fu_list(list)
+ fu_output_message sprintf('chown -R%s %s %s',
+ (force ? 'f' : ''),
+ (group ? "#{user}:#{group}" : user || ':'),
+ list.join(' ')) if verbose
+ return if noop
+ uid = fu_get_uid(user)
+ gid = fu_get_gid(group)
+ list.each do |root|
+ Entry_.new(root).traverse do |ent|
+ begin
+ ent.chown uid, gid
+ rescue
+ raise unless force
+ end
+ end
+ end
+ end
+ module_function :chown_R
+
+ begin
+ require 'etc'
+ rescue LoadError # rescue LoadError for miniruby
+ end
+
+ def fu_get_uid(user) #:nodoc:
+ return nil unless user
+ case user
+ when Integer
+ user
+ when /\A\d+\z/
+ user.to_i
+ else
+ Etc.getpwnam(user) ? Etc.getpwnam(user).uid : nil
+ end
+ end
+ private_module_function :fu_get_uid
+
+ def fu_get_gid(group) #:nodoc:
+ return nil unless group
+ case group
+ when Integer
+ group
+ when /\A\d+\z/
+ group.to_i
+ else
+ Etc.getgrnam(group) ? Etc.getgrnam(group).gid : nil
+ end
+ end
+ private_module_function :fu_get_gid
+
+ #
+ # Updates modification time (mtime) and access time (atime) of file(s) in
+ # +list+. Files are created if they don't exist.
+ #
+ # Bundler::FileUtils.touch 'timestamp'
+ # Bundler::FileUtils.touch Dir.glob('*.c'); system 'make'
+ #
+ def touch(list, noop: nil, verbose: nil, mtime: nil, nocreate: nil)
+ list = fu_list(list)
+ t = mtime
+ if verbose
+ fu_output_message "touch #{nocreate ? '-c ' : ''}#{t ? t.strftime('-t %Y%m%d%H%M.%S ') : ''}#{list.join ' '}"
+ end
+ return if noop
+ list.each do |path|
+ created = nocreate
+ begin
+ File.utime(t, t, path)
+ rescue Errno::ENOENT
+ raise if created
+ File.open(path, 'a') {
+ ;
+ }
+ created = true
+ retry if t
+ end
+ end
+ end
+ module_function :touch
+
+ private
+
+ module StreamUtils_
+ private
+
+ def fu_windows?
+ /mswin|mingw|bccwin|emx/ =~ RUBY_PLATFORM
+ end
+
+ def fu_copy_stream0(src, dest, blksize = nil) #:nodoc:
+ IO.copy_stream(src, dest)
+ end
+
+ def fu_stream_blksize(*streams)
+ streams.each do |s|
+ next unless s.respond_to?(:stat)
+ size = fu_blksize(s.stat)
+ return size if size
+ end
+ fu_default_blksize()
+ end
+
+ def fu_blksize(st)
+ s = st.blksize
+ return nil unless s
+ return nil if s == 0
+ s
+ end
+
+ def fu_default_blksize
+ 1024
+ end
+ end
+
+ include StreamUtils_
+ extend StreamUtils_
+
+ class Entry_ #:nodoc: internal use only
+ include StreamUtils_
+
+ def initialize(a, b = nil, deref = false)
+ @prefix = @rel = @path = nil
+ if b
+ @prefix = a
+ @rel = b
+ else
+ @path = a
+ end
+ @deref = deref
+ @stat = nil
+ @lstat = nil
+ end
+
+ def inspect
+ "\#<#{self.class} #{path()}>"
+ end
+
+ def path
+ if @path
+ File.path(@path)
+ else
+ join(@prefix, @rel)
+ end
+ end
+
+ def prefix
+ @prefix || @path
+ end
+
+ def rel
+ @rel
+ end
+
+ def dereference?
+ @deref
+ end
+
+ def exist?
+ begin
+ lstat
+ true
+ rescue Errno::ENOENT
+ false
+ end
+ end
+
+ def file?
+ s = lstat!
+ s and s.file?
+ end
+
+ def directory?
+ s = lstat!
+ s and s.directory?
+ end
+
+ def symlink?
+ s = lstat!
+ s and s.symlink?
+ end
+
+ def chardev?
+ s = lstat!
+ s and s.chardev?
+ end
+
+ def blockdev?
+ s = lstat!
+ s and s.blockdev?
+ end
+
+ def socket?
+ s = lstat!
+ s and s.socket?
+ end
+
+ def pipe?
+ s = lstat!
+ s and s.pipe?
+ end
+
+ S_IF_DOOR = 0xD000
+
+ def door?
+ s = lstat!
+ s and (s.mode & 0xF000 == S_IF_DOOR)
+ end
+
+ def entries
+ opts = {}
+ opts[:encoding] = ::Encoding::UTF_8 if fu_windows?
+ Dir.entries(path(), opts)\
+ .reject {|n| n == '.' or n == '..' }\
+ .map {|n| Entry_.new(prefix(), join(rel(), n.untaint)) }
+ end
+
+ def stat
+ return @stat if @stat
+ if lstat() and lstat().symlink?
+ @stat = File.stat(path())
+ else
+ @stat = lstat()
+ end
+ @stat
+ end
+
+ def stat!
+ return @stat if @stat
+ if lstat! and lstat!.symlink?
+ @stat = File.stat(path())
+ else
+ @stat = lstat!
+ end
+ @stat
+ rescue SystemCallError
+ nil
+ end
+
+ def lstat
+ if dereference?
+ @lstat ||= File.stat(path())
+ else
+ @lstat ||= File.lstat(path())
+ end
+ end
+
+ def lstat!
+ lstat()
+ rescue SystemCallError
+ nil
+ end
+
+ def chmod(mode)
+ if symlink?
+ File.lchmod mode, path() if have_lchmod?
+ else
+ File.chmod mode, path()
+ end
+ end
+
+ def chown(uid, gid)
+ if symlink?
+ File.lchown uid, gid, path() if have_lchown?
+ else
+ File.chown uid, gid, path()
+ end
+ end
+
+ def copy(dest)
+ lstat
+ case
+ when file?
+ copy_file dest
+ when directory?
+ if !File.exist?(dest) and descendant_directory?(dest, path)
+ raise ArgumentError, "cannot copy directory %s to itself %s" % [path, dest]
+ end
+ begin
+ Dir.mkdir dest
+ rescue
+ raise unless File.directory?(dest)
+ end
+ when symlink?
+ File.symlink File.readlink(path()), dest
+ when chardev?
+ raise "cannot handle device file" unless File.respond_to?(:mknod)
+ mknod dest, ?c, 0666, lstat().rdev
+ when blockdev?
+ raise "cannot handle device file" unless File.respond_to?(:mknod)
+ mknod dest, ?b, 0666, lstat().rdev
+ when socket?
+ raise "cannot handle socket" unless File.respond_to?(:mknod)
+ mknod dest, nil, lstat().mode, 0
+ when pipe?
+ raise "cannot handle FIFO" unless File.respond_to?(:mkfifo)
+ mkfifo dest, 0666
+ when door?
+ raise "cannot handle door: #{path()}"
+ else
+ raise "unknown file type: #{path()}"
+ end
+ end
+
+ def copy_file(dest)
+ File.open(path()) do |s|
+ File.open(dest, 'wb', s.stat.mode) do |f|
+ IO.copy_stream(s, f)
+ end
+ end
+ end
+
+ def copy_metadata(path)
+ st = lstat()
+ if !st.symlink?
+ File.utime st.atime, st.mtime, path
+ end
+ mode = st.mode
+ begin
+ if st.symlink?
+ begin
+ File.lchown st.uid, st.gid, path
+ rescue NotImplementedError
+ end
+ else
+ File.chown st.uid, st.gid, path
+ end
+ rescue Errno::EPERM, Errno::EACCES
+ # clear setuid/setgid
+ mode &= 01777
+ end
+ if st.symlink?
+ begin
+ File.lchmod mode, path
+ rescue NotImplementedError
+ end
+ else
+ File.chmod mode, path
+ end
+ end
+
+ def remove
+ if directory?
+ remove_dir1
+ else
+ remove_file
+ end
+ end
+
+ def remove_dir1
+ platform_support {
+ Dir.rmdir path().chomp(?/)
+ }
+ end
+
+ def remove_file
+ platform_support {
+ File.unlink path
+ }
+ end
+
+ def platform_support
+ return yield unless fu_windows?
+ first_time_p = true
+ begin
+ yield
+ rescue Errno::ENOENT
+ raise
+ rescue => err
+ if first_time_p
+ first_time_p = false
+ begin
+ File.chmod 0700, path() # Windows does not have symlink
+ retry
+ rescue SystemCallError
+ end
+ end
+ raise err
+ end
+ end
+
+ def preorder_traverse
+ stack = [self]
+ while ent = stack.pop
+ yield ent
+ stack.concat ent.entries.reverse if ent.directory?
+ end
+ end
+
+ alias traverse preorder_traverse
+
+ def postorder_traverse
+ if directory?
+ entries().each do |ent|
+ ent.postorder_traverse do |e|
+ yield e
+ end
+ end
+ end
+ ensure
+ yield self
+ end
+
+ def wrap_traverse(pre, post)
+ pre.call self
+ if directory?
+ entries.each do |ent|
+ ent.wrap_traverse pre, post
+ end
+ end
+ post.call self
+ end
+
+ private
+
+ $fileutils_rb_have_lchmod = nil
+
+ def have_lchmod?
+ # This is not MT-safe, but it does not matter.
+ if $fileutils_rb_have_lchmod == nil
+ $fileutils_rb_have_lchmod = check_have_lchmod?
+ end
+ $fileutils_rb_have_lchmod
+ end
+
+ def check_have_lchmod?
+ return false unless File.respond_to?(:lchmod)
+ File.lchmod 0
+ return true
+ rescue NotImplementedError
+ return false
+ end
+
+ $fileutils_rb_have_lchown = nil
+
+ def have_lchown?
+ # This is not MT-safe, but it does not matter.
+ if $fileutils_rb_have_lchown == nil
+ $fileutils_rb_have_lchown = check_have_lchown?
+ end
+ $fileutils_rb_have_lchown
+ end
+
+ def check_have_lchown?
+ return false unless File.respond_to?(:lchown)
+ File.lchown nil, nil
+ return true
+ rescue NotImplementedError
+ return false
+ end
+
+ def join(dir, base)
+ return File.path(dir) if not base or base == '.'
+ return File.path(base) if not dir or dir == '.'
+ File.join(dir, base)
+ end
+
+ if File::ALT_SEPARATOR
+ DIRECTORY_TERM = "(?=[/#{Regexp.quote(File::ALT_SEPARATOR)}]|\\z)"
+ else
+ DIRECTORY_TERM = "(?=/|\\z)"
+ end
+ SYSCASE = File::FNM_SYSCASE.nonzero? ? "-i" : ""
+
+ def descendant_directory?(descendant, ascendant)
+ /\A(?#{SYSCASE}:#{Regexp.quote(ascendant)})#{DIRECTORY_TERM}/ =~ File.dirname(descendant)
+ end
+ end # class Entry_
+
+ def fu_list(arg) #:nodoc:
+ [arg].flatten.map {|path| File.path(path) }
+ end
+ private_module_function :fu_list
+
+ def fu_each_src_dest(src, dest) #:nodoc:
+ fu_each_src_dest0(src, dest) do |s, d|
+ raise ArgumentError, "same file: #{s} and #{d}" if fu_same?(s, d)
+ yield s, d
+ end
+ end
+ private_module_function :fu_each_src_dest
+
+ def fu_each_src_dest0(src, dest) #:nodoc:
+ if tmp = Array.try_convert(src)
+ tmp.each do |s|
+ s = File.path(s)
+ yield s, File.join(dest, File.basename(s))
+ end
+ else
+ src = File.path(src)
+ if File.directory?(dest)
+ yield src, File.join(dest, File.basename(src))
+ else
+ yield src, File.path(dest)
+ end
+ end
+ end
+ private_module_function :fu_each_src_dest0
+
+ def fu_same?(a, b) #:nodoc:
+ File.identical?(a, b)
+ end
+ private_module_function :fu_same?
+
+ @fileutils_output = $stderr
+ @fileutils_label = ''
+
+ def fu_output_message(msg) #:nodoc:
+ @fileutils_output ||= $stderr
+ @fileutils_label ||= ''
+ @fileutils_output.puts @fileutils_label + msg
+ end
+ private_module_function :fu_output_message
+
+ # This hash table holds command options.
+ OPT_TABLE = {} #:nodoc: internal use only
+ (private_instance_methods & methods(false)).inject(OPT_TABLE) {|tbl, name|
+ (tbl[name.to_s] = instance_method(name).parameters).map! {|t, n| n if t == :key}.compact!
+ tbl
+ }
+
+ #
+ # Returns an Array of method names which have any options.
+ #
+ # p Bundler::FileUtils.commands #=> ["chmod", "cp", "cp_r", "install", ...]
+ #
+ def self.commands
+ OPT_TABLE.keys
+ end
+
+ #
+ # Returns an Array of option names.
+ #
+ # p Bundler::FileUtils.options #=> ["noop", "force", "verbose", "preserve", "mode"]
+ #
+ def self.options
+ OPT_TABLE.values.flatten.uniq.map {|sym| sym.to_s }
+ end
+
+ #
+ # Returns true if the method +mid+ have an option +opt+.
+ #
+ # p Bundler::FileUtils.have_option?(:cp, :noop) #=> true
+ # p Bundler::FileUtils.have_option?(:rm, :force) #=> true
+ # p Bundler::FileUtils.have_option?(:rm, :preserve) #=> false
+ #
+ def self.have_option?(mid, opt)
+ li = OPT_TABLE[mid.to_s] or raise ArgumentError, "no such method: #{mid}"
+ li.include?(opt)
+ end
+
+ #
+ # Returns an Array of option names of the method +mid+.
+ #
+ # p Bundler::FileUtils.options_of(:rm) #=> ["noop", "verbose", "force"]
+ #
+ def self.options_of(mid)
+ OPT_TABLE[mid.to_s].map {|sym| sym.to_s }
+ end
+
+ #
+ # Returns an Array of method names which have the option +opt+.
+ #
+ # p Bundler::FileUtils.collect_method(:preserve) #=> ["cp", "cp_r", "copy", "install"]
+ #
+ def self.collect_method(opt)
+ OPT_TABLE.keys.select {|m| OPT_TABLE[m].include?(opt) }
+ end
+
+ LOW_METHODS = singleton_methods(false) - collect_method(:noop).map(&:intern)
+ module LowMethods
+ private
+ def _do_nothing(*)end
+ ::Bundler::FileUtils::LOW_METHODS.map {|name| alias_method name, :_do_nothing}
+ end
+
+ METHODS = singleton_methods() - [:private_module_function,
+ :commands, :options, :have_option?, :options_of, :collect_method]
+
+ #
+ # This module has all methods of Bundler::FileUtils module, but it outputs messages
+ # before acting. This equates to passing the <tt>:verbose</tt> flag to
+ # methods in Bundler::FileUtils.
+ #
+ module Verbose
+ include Bundler::FileUtils
+ @fileutils_output = $stderr
+ @fileutils_label = ''
+ names = ::Bundler::FileUtils.collect_method(:verbose)
+ names.each do |name|
+ module_eval(<<-EOS, __FILE__, __LINE__ + 1)
+ def #{name}(*args, **options)
+ super(*args, **options, verbose: true)
+ end
+ EOS
+ end
+ private(*names)
+ extend self
+ class << self
+ public(*::Bundler::FileUtils::METHODS)
+ end
+ end
+
+ #
+ # This module has all methods of Bundler::FileUtils module, but never changes
+ # files/directories. This equates to passing the <tt>:noop</tt> flag
+ # to methods in Bundler::FileUtils.
+ #
+ module NoWrite
+ include Bundler::FileUtils
+ include LowMethods
+ @fileutils_output = $stderr
+ @fileutils_label = ''
+ names = ::Bundler::FileUtils.collect_method(:noop)
+ names.each do |name|
+ module_eval(<<-EOS, __FILE__, __LINE__ + 1)
+ def #{name}(*args, **options)
+ super(*args, **options, noop: true)
+ end
+ EOS
+ end
+ private(*names)
+ extend self
+ class << self
+ public(*::Bundler::FileUtils::METHODS)
+ end
+ end
+
+ #
+ # This module has all methods of Bundler::FileUtils module, but never changes
+ # files/directories, with printing message before acting.
+ # This equates to passing the <tt>:noop</tt> and <tt>:verbose</tt> flag
+ # to methods in Bundler::FileUtils.
+ #
+ module DryRun
+ include Bundler::FileUtils
+ include LowMethods
+ @fileutils_output = $stderr
+ @fileutils_label = ''
+ names = ::Bundler::FileUtils.collect_method(:noop)
+ names.each do |name|
+ module_eval(<<-EOS, __FILE__, __LINE__ + 1)
+ def #{name}(*args, **options)
+ super(*args, **options, noop: true, verbose: true)
+ end
+ EOS
+ end
+ private(*names)
+ extend self
+ class << self
+ public(*::Bundler::FileUtils::METHODS)
+ end
+ end
+
+end
diff --git a/lib/bundler/vendored_fileutils.rb b/lib/bundler/vendored_fileutils.rb
new file mode 100644
index 0000000000..b2522cd5e0
--- /dev/null
+++ b/lib/bundler/vendored_fileutils.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+module Bundler; end
+if RUBY_VERSION >= "2.4"
+ require "bundler/vendor/fileutils/lib/fileutils"
+else
+ # the version we vendor is 2.4+
+ require "fileutils"
+end
diff --git a/man/bundle-config.ronn b/man/bundle-config.ronn
index 4085d2726c..785229e59a 100644
--- a/man/bundle-config.ronn
+++ b/man/bundle-config.ronn
@@ -243,6 +243,9 @@ learn more about their operation in [bundle install(1)][bundle-install].
* `skip_default_git_sources` (`BUNDLE_SKIP_DEFAULT_GIT_SOURCES`):
Whether Bundler should skip adding default git source shortcuts to the
Gemfile DSL.
+* `suppress_install_using_messages` (`BUNDLE_SUPPRESS_INSTALL_USING_MESSAGES`):
+ Avoid printing `Using ...` messages during installation when the version of
+ a gem has not changed.
In general, you should set these settings per-application by using the applicable
flag to the [bundle install(1)][bundle-install] or [bundle package(1)][bundle-package] command.
diff --git a/spec/bundler/bundler_spec.rb b/spec/bundler/bundler_spec.rb
index c73bbf3670..3f4ebdd0ac 100644
--- a/spec/bundler/bundler_spec.rb
+++ b/spec/bundler/bundler_spec.rb
@@ -157,7 +157,7 @@ RSpec.describe Bundler do
let(:bundler_ui) { Bundler.ui }
it "should raise a friendly error" do
allow(File).to receive(:exist?).and_return(true)
- allow(FileUtils).to receive(:remove_entry_secure).and_raise(ArgumentError)
+ allow(bundler_fileutils).to receive(:remove_entry_secure).and_raise(ArgumentError)
allow(File).to receive(:world_writable?).and_return(true)
message = <<EOF
It is a security vulnerability to allow your home directory to be world-writable, and bundler can not continue.
diff --git a/spec/bundler/settings_spec.rb b/spec/bundler/settings_spec.rb
index 1acd2a97b3..d680ab3f3e 100644
--- a/spec/bundler/settings_spec.rb
+++ b/spec/bundler/settings_spec.rb
@@ -119,7 +119,7 @@ that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
context "when it's not possible to write to the file" do
it "raises an PermissionError with explanation" do
- expect(FileUtils).to receive(:mkdir_p).with(settings.send(:local_config_file).dirname).
+ expect(bundler_fileutils).to receive(:mkdir_p).with(settings.send(:local_config_file).dirname).
and_raise(Errno::EACCES)
expect { settings[:frozen] = "1" }.
to raise_error(Bundler::PermissionError, /config/)
@@ -142,7 +142,7 @@ that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
describe "#set_global" do
context "when it's not possible to write to the file" do
it "raises an PermissionError with explanation" do
- expect(FileUtils).to receive(:mkdir_p).with(settings.send(:global_config_file).dirname).
+ expect(bundler_fileutils).to receive(:mkdir_p).with(settings.send(:global_config_file).dirname).
and_raise(Errno::EACCES)
expect { settings.set_global(:frozen, "1") }.
to raise_error(Bundler::PermissionError, %r{\.bundle/config})
diff --git a/spec/bundler/ssl_certs/certificate_manager_spec.rb b/spec/bundler/ssl_certs/certificate_manager_spec.rb
index 697bf7757b..7cf601f6db 100644
--- a/spec/bundler/ssl_certs/certificate_manager_spec.rb
+++ b/spec/bundler/ssl_certs/certificate_manager_spec.rb
@@ -71,17 +71,17 @@ RSpec.describe Bundler::SSLCerts::CertificateManager do
context "when certificate manager is not up to date" do
before do
allow(subject).to receive(:up_to_date?).and_return(false)
- allow(FileUtils).to receive(:rm)
- allow(FileUtils).to receive(:cp)
+ allow(bundler_fileutils).to receive(:rm)
+ allow(bundler_fileutils).to receive(:cp)
end
it "should remove the current bundler certs" do
- expect(FileUtils).to receive(:rm).with(subject.bundler_certs)
+ expect(bundler_fileutils).to receive(:rm).with(subject.bundler_certs)
subject.update!
end
it "should copy the rubygems certs into bundler certs" do
- expect(FileUtils).to receive(:cp).with(subject.rubygems_certs, subject.bundler_cert_path)
+ expect(bundler_fileutils).to receive(:cp).with(subject.rubygems_certs, subject.bundler_cert_path)
subject.update!
end
diff --git a/spec/commands/pristine_spec.rb b/spec/commands/pristine_spec.rb
index 9c3918f51e..56496a0a27 100644
--- a/spec/commands/pristine_spec.rb
+++ b/spec/commands/pristine_spec.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require "fileutils"
+require "bundler/vendored_fileutils"
RSpec.describe "bundle pristine" do
before :each do
@@ -116,7 +116,6 @@ RSpec.describe "bundle pristine" do
foo_changes_txt = Pathname.new(foo.full_gem_path).join("lib/changes.txt")
FileUtils.touch(foo_changes_txt)
expect(foo_changes_txt).to be_file
- foo_ref = Spec::Builders::GitReader.new(lib_path("foo")).ref_for("HEAD", 6)
bar = Bundler.definition.specs["bar"].first
bar_changes_txt = Pathname.new(bar.full_gem_path).join("lib/changes.txt")
@@ -130,11 +129,9 @@ RSpec.describe "bundle pristine" do
bundle! "pristine foo bar weakling"
- expect(out).to eq(strip_whitespace(<<-EOS).strip)
- Cannot pristine bar (1.0). Gem is sourced from local path.
- Using foo 1.0 from #{lib_path("foo")} (at master@#{foo_ref})
- Installing weakling 1.0
- EOS
+ expect(out).to include("Cannot pristine bar (1.0). Gem is sourced from local path.").
+ and include("Installing weakling 1.0")
+
expect(weakling_changes_txt).not_to be_file
expect(foo_changes_txt).not_to be_file
expect(bar_changes_txt).to be_file
diff --git a/spec/commands/update_spec.rb b/spec/commands/update_spec.rb
index a7d50502e3..eb6d2382cf 100644
--- a/spec/commands/update_spec.rb
+++ b/spec/commands/update_spec.rb
@@ -430,7 +430,7 @@ RSpec.describe "bundle update when a gem depends on a newer version of bundler"
end
RSpec.describe "bundle update" do
- it "shows the previous version of the gem when updated from rubygems source" do
+ it "shows the previous version of the gem when updated from rubygems source", :bundler => "< 2" do
build_repo2
install_gemfile <<-G
@@ -449,6 +449,40 @@ RSpec.describe "bundle update" do
expect(out).to include("Installing activesupport 3.0 (was 2.3.5)")
end
+ context "with suppress_install_using_messages set" do
+ before { bundle! "config suppress_install_using_messages true" }
+
+ it "only prints `Using` for versions that have changed" do
+ build_repo4 do
+ build_gem "bar"
+ build_gem "foo"
+ end
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo4}"
+ gem "bar"
+ gem "foo"
+ G
+
+ bundle! "update", :all => bundle_update_requires_all?
+ out.gsub!(/RubyGems [\d\.]+ is not threadsafe.*\n?/, "")
+ expect(out).to include "Resolving dependencies...\nBundle updated!"
+
+ update_repo4 do
+ build_gem "foo", "2.0"
+ end
+
+ bundle! "update", :all => bundle_update_requires_all?
+ out.gsub!(/RubyGems [\d\.]+ is not threadsafe.*\n?/, "")
+ expect(out).to include strip_whitespace(<<-EOS).strip
+ Resolving dependencies...
+ Fetching foo 2.0 (was 1.0)
+ Installing foo 2.0 (was 1.0)
+ Bundle updated
+ EOS
+ end
+ end
+
it "shows error message when Gemfile.lock is not preset and gem is specified" do
install_gemfile <<-G
source "file://#{gem_repo2}"
diff --git a/spec/install/deploy_spec.rb b/spec/install/deploy_spec.rb
index 8ef948de77..b4ab5d495f 100644
--- a/spec/install/deploy_spec.rb
+++ b/spec/install/deploy_spec.rb
@@ -312,15 +312,13 @@ You have deleted from the Gemfile:
expect(bundled_app("vendor/cache/foo")).to be_directory
bundle! "install --local"
- expect(out).to include("Using foo 1.0 from source at")
- expect(out).to include("vendor/cache/foo")
+ expect(out).to include("Updating files in vendor/cache")
simulate_new_machine
bundle! "install --deployment --verbose"
expect(out).not_to include("You are trying to install in deployment mode after changing your Gemfile")
expect(out).not_to include("You have added to the Gemfile")
expect(out).not_to include("You have deleted from the Gemfile")
- expect(out).to include("Using foo 1.0 from source at")
expect(out).to include("vendor/cache/foo")
expect(the_bundle).to include_gems "foo 1.0"
end
diff --git a/spec/install/force_spec.rb b/spec/install/force_spec.rb
index 2bc300597d..5592e62d68 100644
--- a/spec/install/force_spec.rb
+++ b/spec/install/force_spec.rb
@@ -17,7 +17,6 @@ RSpec.describe "bundle install" do
bundle "install --force"
expect(exitstatus).to eq(0) if exitstatus
- expect(out).to include "Using bundler"
expect(out).to include "Installing rack 1.0.0"
expect(rack_lib.open(&:read)).to eq("RACK = '1.0.0'\n")
expect(the_bundle).to include_gems "rack 1.0.0"
@@ -27,7 +26,6 @@ RSpec.describe "bundle install" do
bundle "install --force"
expect(exitstatus).to eq(0) if exitstatus
- expect(out).to include "Using bundler"
expect(out).to include "Installing rack 1.0.0"
expect(the_bundle).to include_gems "rack 1.0.0"
end
@@ -48,8 +46,6 @@ RSpec.describe "bundle install" do
foo_lib.open("w") {|f| f.write("blah blah blah") }
bundle! "install --force"
- expect(out).to include "Using bundler"
- expect(out).to include "Using foo 1.0 from #{lib_path("foo-1.0")} (at master@#{ref[0, 7]})"
expect(foo_lib.open(&:read)).to eq("FOO = '1.0'\n")
expect(the_bundle).to include_gems "foo 1.0"
end
@@ -57,8 +53,6 @@ RSpec.describe "bundle install" do
it "works on first bundle install" do
bundle! "install --force"
- expect(out).to include "Using bundler"
- expect(out).to include "Using foo 1.0 from #{lib_path("foo-1.0")} (at master@#{ref[0, 7]})"
expect(the_bundle).to include_gems "foo 1.0"
end
end
diff --git a/spec/install/gemfile/eval_gemfile_spec.rb b/spec/install/gemfile/eval_gemfile_spec.rb
index f3f9cb6444..b76fae02dc 100644
--- a/spec/install/gemfile/eval_gemfile_spec.rb
+++ b/spec/install/gemfile/eval_gemfile_spec.rb
@@ -20,8 +20,9 @@ RSpec.describe "bundle install with gemfile that uses eval_gemfile" do
eval_gemfile 'Gemfile-other'
G
expect(out).to include("Resolving dependencies")
- expect(out).to include("Using gunks 0.0.1 from source at `gems/gunks`")
expect(out).to include("Bundle complete")
+
+ expect(the_bundle).to include_gem "gunks 0.0.1", :source => "path@#{bundled_app("gems", "gunks")}"
end
end
@@ -52,8 +53,9 @@ RSpec.describe "bundle install with gemfile that uses eval_gemfile" do
gemspec :path => 'gems/gunks'
G
expect(out).to include("Resolving dependencies")
- expect(out).to include("Using gunks 0.0.1 from source at `gems/gunks`")
expect(out).to include("Bundle complete")
+
+ expect(the_bundle).to include_gem "gunks 0.0.1", :source => "path@#{bundled_app("gems", "gunks")}"
end
end
end
diff --git a/spec/install/gemfile/git_spec.rb b/spec/install/gemfile/git_spec.rb
index c160170a7a..b3fc38ec24 100644
--- a/spec/install/gemfile/git_spec.rb
+++ b/spec/install/gemfile/git_spec.rb
@@ -397,9 +397,8 @@ RSpec.describe "bundle install with git sources" do
gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
G
- bundle %(config local.rack #{lib_path("local-rack")})
- bundle :install
- expect(out).to match(/at #{lib_path('local-rack')}/)
+ bundle! %(config local.rack #{lib_path("local-rack")})
+ bundle! :install
run "require 'rack'"
expect(out).to eq("LOCAL")
diff --git a/spec/install/gems/native_extensions_spec.rb b/spec/install/gems/native_extensions_spec.rb
index f27e0b10d5..c8252b81f1 100644
--- a/spec/install/gems/native_extensions_spec.rb
+++ b/spec/install/gems/native_extensions_spec.rb
@@ -83,7 +83,6 @@ RSpec.describe "installing a gem with native extensions" do
G
expect(out).not_to include("extconf.rb failed")
- expect(out).to include("Using c_extension 1.0")
run! "Bundler.require; puts CExtension.new.its_true"
expect(out).to eq("true")
diff --git a/spec/install/git_spec.rb b/spec/install/git_spec.rb
index 693289c72c..85523b350e 100644
--- a/spec/install/git_spec.rb
+++ b/spec/install/git_spec.rb
@@ -2,7 +2,7 @@
RSpec.describe "bundle install" do
context "git sources" do
- it "displays the revision hash of the gem repository" do
+ it "displays the revision hash of the gem repository", :bundler => "< 2" do
build_git "foo", "1.0", :path => lib_path("foo")
install_gemfile <<-G
@@ -14,7 +14,7 @@ RSpec.describe "bundle install" do
expect(the_bundle).to include_gems "foo 1.0", :source => "git@#{lib_path("foo")}"
end
- it "displays the ref of the gem repository when using branch~num as a ref" do
+ it "displays the ref of the gem repository when using branch~num as a ref", :bundler => "< 2" do
build_git "foo", "1.0", :path => lib_path("foo")
rev = revision_for(lib_path("foo"))[0..6]
update_git "foo", "2.0", :path => lib_path("foo"), :gemspec => true
diff --git a/spec/plugins/source/example_spec.rb b/spec/plugins/source/example_spec.rb
index 748f083456..d956a0571b 100644
--- a/spec/plugins/source/example_spec.rb
+++ b/spec/plugins/source/example_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe "real source plugins" do
build_repo2 do
build_plugin "bundler-source-mpath" do |s|
s.write "plugins.rb", <<-RUBY
- require "fileutils"
+ require "bundler/vendored_fileutils"
require "bundler-source-mpath"
class MPath < Bundler::Plugin::API
diff --git a/spec/runtime/setup_spec.rb b/spec/runtime/setup_spec.rb
index 9f00f9a06b..5fcbd4eb45 100644
--- a/spec/runtime/setup_spec.rb
+++ b/spec/runtime/setup_spec.rb
@@ -529,8 +529,7 @@ RSpec.describe "Bundler.setup" do
G
bundle %(config local.rack #{lib_path("local-rack")})
- bundle :install
- expect(out).to match(/at #{lib_path('local-rack')}/)
+ bundle! :install
FileUtils.rm_rf(lib_path("local-rack"))
run "require 'rack'"
@@ -548,8 +547,7 @@ RSpec.describe "Bundler.setup" do
G
bundle %(config local.rack #{lib_path("local-rack")})
- bundle :install
- expect(out).to match(/at #{lib_path('local-rack')}/)
+ bundle! :install
gemfile <<-G
source "file://#{gem_repo1}"
@@ -571,8 +569,7 @@ RSpec.describe "Bundler.setup" do
G
bundle %(config local.rack #{lib_path("local-rack")})
- bundle :install
- expect(out).to match(/at #{lib_path('local-rack')}/)
+ bundle! :install
gemfile <<-G
source "file://#{gem_repo1}"
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index ee7777a4bd..5c7fcbffa5 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -3,7 +3,7 @@ $:.unshift File.expand_path("..", __FILE__)
$:.unshift File.expand_path("../../lib", __FILE__)
require "bundler/psyched_yaml"
-require "fileutils"
+require "bundler/vendored_fileutils"
require "uri"
require "digest/sha1"
diff --git a/spec/support/artifice/vcr.rb b/spec/support/artifice/vcr.rb
index 9db0053e6c..dff87e87f1 100644
--- a/spec/support/artifice/vcr.rb
+++ b/spec/support/artifice/vcr.rb
@@ -74,7 +74,7 @@ class BundlerVCRHTTP < Net::HTTP
end
def file_name_for_key(key)
- key.join("/")
+ key.join("/").gsub(/[\:*?"<>|]/, "-")
end
def request_pair_paths
diff --git a/spec/support/artifice/vcr_cassettes/realworld/api.rubygems.org/api/v1/dependencies?gems=bundler/GET/request b/spec/support/artifice/vcr_cassettes/realworld/api.rubygems.org/api/v1/dependencies-gems=bundler/GET/request
index 00dcd51750..00dcd51750 100644
--- a/spec/support/artifice/vcr_cassettes/realworld/api.rubygems.org/api/v1/dependencies?gems=bundler/GET/request
+++ b/spec/support/artifice/vcr_cassettes/realworld/api.rubygems.org/api/v1/dependencies-gems=bundler/GET/request
diff --git a/spec/support/artifice/vcr_cassettes/realworld/api.rubygems.org/api/v1/dependencies?gems=bundler/GET/response b/spec/support/artifice/vcr_cassettes/realworld/api.rubygems.org/api/v1/dependencies-gems=bundler/GET/response
index 2dd5aa76f8..2dd5aa76f8 100644
--- a/spec/support/artifice/vcr_cassettes/realworld/api.rubygems.org/api/v1/dependencies?gems=bundler/GET/response
+++ b/spec/support/artifice/vcr_cassettes/realworld/api.rubygems.org/api/v1/dependencies-gems=bundler/GET/response
Binary files differ
diff --git a/spec/support/helpers.rb b/spec/support/helpers.rb
index b31b0f05c1..03d9711878 100644
--- a/spec/support/helpers.rb
+++ b/spec/support/helpers.rb
@@ -514,5 +514,13 @@ module Spec
end
port
end
+
+ def bundler_fileutils
+ if RUBY_VERSION >= "2.4"
+ ::Bundler::FileUtils
+ else
+ ::FileUtils
+ end
+ end
end
end
diff --git a/spec/update/git_spec.rb b/spec/update/git_spec.rb
index f7e10de666..2b12713198 100644
--- a/spec/update/git_spec.rb
+++ b/spec/update/git_spec.rb
@@ -27,12 +27,11 @@ RSpec.describe "bundle update" do
s.add_dependency "activesupport", "= 3.0"
end
- install_gemfile <<-G
+ install_gemfile! <<-G
gem "rails", :git => "#{lib_path("rails")}"
G
- bundle "update rails"
- expect(out).to include("Using activesupport 3.0 from #{lib_path("rails")} (at master@#{revision_for(lib_path("rails"))[0..6]})")
+ bundle! "update rails"
expect(the_bundle).to include_gems "rails 3.0", "activesupport 3.0"
end