diff options
author | Bundlerbot <bot@bundler.io> | 2019-03-26 00:37:48 +0000 |
---|---|---|
committer | Colby Swandale <me@colby.fyi> | 2019-04-05 22:24:08 +1100 |
commit | 8f3facd8e215b040a4cbb228f2a17f59c8048e67 (patch) | |
tree | aafb70754cbbec163462ec9f021b0d49610ada2e | |
parent | c17d799436de359137403c676ce3e671ec09a214 (diff) | |
download | bundler-8f3facd8e215b040a4cbb228f2a17f59c8048e67.tar.gz |
Merge #7067
7067: Tweaking vendoring r=hsbt a=deivid-rodriguez
The problem was that we were not using the latest versions of some of our vendored dependencies.
My diagnosis was that we should upgrade them.
My fix is to upgrade them using `automatiek`, and add a few tweaks to our vendoring setup.
I chose this fix because.... I didn't really considered other options.
Co-authored-by: David RodrÃguez <deivid.rodriguez@riseup.net>
(cherry picked from commit 6f39ea5b15d61e5bdf7915bc94d66c31b1242d15)
-rw-r--r-- | lib/bundler/vendor/fileutils/lib/fileutils.rb | 149 | ||||
-rw-r--r-- | lib/bundler/vendor/thor/lib/thor/actions.rb | 18 | ||||
-rw-r--r-- | lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb | 13 | ||||
-rw-r--r-- | lib/bundler/vendor/thor/lib/thor/base.rb | 7 | ||||
-rw-r--r-- | lib/bundler/vendor/thor/lib/thor/error.rb | 82 | ||||
-rw-r--r-- | lib/bundler/vendor/thor/lib/thor/group.rb | 4 | ||||
-rw-r--r-- | lib/bundler/vendor/thor/lib/thor/parser/options.rb | 9 | ||||
-rw-r--r-- | lib/bundler/vendor/thor/lib/thor/runner.rb | 4 | ||||
-rw-r--r-- | lib/bundler/vendor/thor/lib/thor/shell.rb | 2 | ||||
-rw-r--r-- | lib/bundler/vendor/thor/lib/thor/shell/basic.rb | 59 | ||||
-rw-r--r-- | lib/bundler/vendor/thor/lib/thor/version.rb | 2 |
11 files changed, 298 insertions, 51 deletions
diff --git a/lib/bundler/vendor/fileutils/lib/fileutils.rb b/lib/bundler/vendor/fileutils/lib/fileutils.rb index cc69740845..77b3bade52 100644 --- a/lib/bundler/vendor/fileutils/lib/fileutils.rb +++ b/lib/bundler/vendor/fileutils/lib/fileutils.rb @@ -85,8 +85,12 @@ # <tt>:verbose</tt> flags to methods in Bundler::FileUtils. # +require 'rbconfig' + module Bundler::FileUtils + VERSION = "1.1.0" + def self.private_module_function(name) #:nodoc: module_function name private_class_method name @@ -117,8 +121,9 @@ module Bundler::FileUtils # def cd(dir, verbose: nil, &block) # :yield: dir fu_output_message "cd #{dir}" if verbose - Dir.chdir(dir, &block) + result = Dir.chdir(dir, &block) fu_output_message 'cd -' if verbose and block + result end module_function :cd @@ -245,15 +250,15 @@ module Bundler::FileUtils 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 + Dir.rmdir(dir = remove_trailing_slash(dir)) + if parents + begin until (parent = File.dirname(dir)) == '.' or parent == dir dir = parent Dir.rmdir(dir) end + rescue Errno::ENOTEMPTY, Errno::EEXIST, Errno::ENOENT end - rescue Errno::ENOTEMPTY, Errno::EEXIST, Errno::ENOENT end end end @@ -295,6 +300,39 @@ module Bundler::FileUtils # # :call-seq: + # Bundler::FileUtils.cp_lr(src, dest, noop: nil, verbose: nil, dereference_root: true, remove_destination: false) + # + # Hard link +src+ to +dest+. If +src+ is a directory, this method links + # all its contents recursively. If +dest+ is a directory, links + # +src+ to +dest/src+. + # + # +src+ can be a list of files. + # + # # Installing the library "mylib" under the site_ruby directory. + # Bundler::FileUtils.rm_r site_ruby + '/mylib', :force => true + # Bundler::FileUtils.cp_lr 'lib/', site_ruby + '/mylib' + # + # # Examples of linking several files to target directory. + # Bundler::FileUtils.cp_lr %w(mail.rb field.rb debug/), site_ruby + '/tmail' + # Bundler::FileUtils.cp_lr Dir.glob('*.rb'), '/home/aamine/lib/ruby', :noop => true, :verbose => true + # + # # If you want to link all contents of a directory instead of the + # # directory itself, c.f. src/x -> dest/x, src/y -> dest/y, + # # use the following code. + # Bundler::FileUtils.cp_lr 'src/.', 'dest' # cp_lr('src', 'dest') makes dest/src, but this doesn't. + # + def cp_lr(src, dest, noop: nil, verbose: nil, + dereference_root: true, remove_destination: false) + fu_output_message "cp -lr#{remove_destination ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}" if verbose + return if noop + fu_each_src_dest(src, dest) do |s, d| + link_entry s, d, dereference_root, remove_destination + end + end + module_function :cp_lr + + # + # :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) @@ -340,6 +378,26 @@ module Bundler::FileUtils module_function :ln_sf # + # Hard links a file system entry +src+ to +dest+. + # If +src+ is a directory, this method links its contents recursively. + # + # Both of +src+ and +dest+ must be a path name. + # +src+ must exist, +dest+ must not exist. + # + # If +dereference_root+ is true, this method dereferences the tree root. + # + # If +remove_destination+ is true, this method removes each destination file before copy. + # + def link_entry(src, dest, dereference_root = false, remove_destination = false) + Entry_.new(src, nil, dereference_root).traverse do |ent| + destent = Entry_.new(dest, ent.rel, false) + File.unlink destent.path if remove_destination && File.file?(destent.path) + ent.link destent.path + end + end + module_function :link_entry + + # # Copies a file content +src+ to +dest+. If +dest+ is a directory, # copies +src+ to +dest/src+. # @@ -412,7 +470,7 @@ module Bundler::FileUtils 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) + File.unlink destent.path if remove_destination && (File.file?(destent.path) || File.symlink?(destent.path)) ent.copy destent.path end, proc do |ent| destent = Entry_.new(dest, ent.rel, false) @@ -486,7 +544,7 @@ module Bundler::FileUtils module_function :move def rename_cannot_overwrite_file? #:nodoc: - /emx/ =~ RUBY_PLATFORM + /emx/ =~ RbConfig::CONFIG['host_os'] end private_module_function :rename_cannot_overwrite_file? @@ -601,8 +659,8 @@ module Bundler::FileUtils # # 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 + # * https://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2005-0448 + # * https://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0452 # # For fileutils.rb, this vulnerability is reported in [ruby-dev:26100]. # @@ -626,22 +684,38 @@ module Bundler::FileUtils 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 - } + dot_file = fullpath + "/." + begin + File.open(dot_file) {|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 + } + rescue EISDIR # JRuby in non-native mode can't open files as dirs + File.lstat(dot_file).tap {|fstat| + unless fu_stat_identical_entry?(st, fstat) + # symlink (TOC-to-TOU attack?) + File.unlink fullpath + return + end + File.chown euid, -1, dot_file + File.chmod 0700, dot_file + } + end + + 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| @@ -742,8 +816,15 @@ module Bundler::FileUtils # def compare_stream(a, b) bsize = fu_stream_blksize(a, b) - sa = String.new(capacity: bsize) - sb = String.new(capacity: bsize) + + if RUBY_VERSION > "2.4" + sa = String.new(capacity: bsize) + sb = String.new(capacity: bsize) + else + sa = String.new + sb = String.new + end + begin a.read(bsize, sa) b.read(bsize, sb) @@ -1068,7 +1149,7 @@ module Bundler::FileUtils private def fu_windows? - /mswin|mingw|bccwin|emx/ =~ RUBY_PLATFORM + /mswin|mingw|bccwin|emx/ =~ RbConfig::CONFIG['host_os'] end def fu_copy_stream0(src, dest, blksize = nil) #:nodoc: @@ -1250,6 +1331,22 @@ module Bundler::FileUtils end end + def link(dest) + case + when directory? + if !File.exist?(dest) and descendant_directory?(dest, path) + raise ArgumentError, "cannot link directory %s to itself %s" % [path, dest] + end + begin + Dir.mkdir dest + rescue + raise unless File.directory?(dest) + end + else + File.link path(), dest + end + end + def copy(dest) lstat case diff --git a/lib/bundler/vendor/thor/lib/thor/actions.rb b/lib/bundler/vendor/thor/lib/thor/actions.rb index e6698572a9..b06feac2a0 100644 --- a/lib/bundler/vendor/thor/lib/thor/actions.rb +++ b/lib/bundler/vendor/thor/lib/thor/actions.rb @@ -113,8 +113,10 @@ class Bundler::Thor # the script started). # def relative_to_original_destination_root(path, remove_dot = true) - path = path.dup - if path.gsub!(@destination_stack[0], ".") + root = @destination_stack[0] + if path.start_with?(root) && [File::SEPARATOR, File::ALT_SEPARATOR, nil, ''].include?(path[root.size..root.size]) + path = path.dup + path[0...root.size] = '.' remove_dot ? (path[2..-1] || "") : path else path @@ -217,6 +219,7 @@ class Bundler::Thor shell.padding += 1 if verbose contents = if is_uri + require "open-uri" open(path, "Accept" => "application/x-thor-template", &:read) else open(path, &:read) @@ -252,9 +255,16 @@ class Bundler::Thor say_status :run, desc, config.fetch(:verbose, true) - unless options[:pretend] - config[:capture] ? `#{command}` : system(command.to_s) + return if options[:pretend] + + result = config[:capture] ? `#{command}` : system(command.to_s) + + if config[:abort_on_failure] + success = config[:capture] ? $?.success? : result + abort unless success end + + result end # Executes a ruby script (taking into account WIN32 platform quirks). diff --git a/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb b/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb index 4c83bebc86..cc29db05a8 100644 --- a/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb +++ b/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb @@ -60,6 +60,9 @@ class Bundler::Thor # destination. If a block is given instead of destination, the content of # the url is yielded and used as location. # + # +get+ relies on open-uri, so passing application user input would provide + # a command injection attack vector. + # # ==== Parameters # source<String>:: the address of the given content. # destination<String>:: the relative path to the destination root. @@ -117,7 +120,13 @@ class Bundler::Thor context = config.delete(:context) || instance_eval("binding") create_file destination, nil, config do - content = CapturableERB.new(::File.binread(source), nil, "-", "@output_buffer").tap do |erb| + match = ERB.version.match(/(\d+\.\d+\.\d+)/) + capturable_erb = if match && match[1] >= "2.2.0" # Ruby 2.6+ + CapturableERB.new(::File.binread(source), :trim_mode => "-", :eoutvar => "@output_buffer") + else + CapturableERB.new(::File.binread(source), nil, "-", "@output_buffer") + end + content = capturable_erb.tap do |erb| erb.filename = source end.result(context) content = yield(content) if block @@ -301,7 +310,7 @@ class Bundler::Thor def comment_lines(path, flag, *args) flag = flag.respond_to?(:source) ? flag.source : flag - gsub_file(path, /^(\s*)([^#|\n]*#{flag})/, '\1# \2', *args) + gsub_file(path, /^(\s*)([^#\n]*#{flag})/, '\1# \2', *args) end # Removes a file at the given location. diff --git a/lib/bundler/vendor/thor/lib/thor/base.rb b/lib/bundler/vendor/thor/lib/thor/base.rb index 9bd1077170..6d3f273ab1 100644 --- a/lib/bundler/vendor/thor/lib/thor/base.rb +++ b/lib/bundler/vendor/thor/lib/thor/base.rb @@ -466,13 +466,13 @@ class Bundler::Thor dispatch(nil, given_args.dup, nil, config) rescue Bundler::Thor::Error => e config[:debug] || ENV["THOR_DEBUG"] == "1" ? (raise e) : config[:shell].error(e.message) - exit(1) if exit_on_failure? + exit(false) if exit_on_failure? rescue Errno::EPIPE # This happens if a thor command is piped to something like `head`, # which closes the pipe when it's done reading. This will also # mean that if the pipe is closed, further unnecessary # computation will not occur. - exit(0) + exit(true) end # Allows to use private methods from parent in child classes as commands. @@ -493,8 +493,7 @@ class Bundler::Thor alias_method :public_task, :public_command def handle_no_command_error(command, has_namespace = $thor_runner) #:nodoc: - raise UndefinedCommandError, "Could not find command #{command.inspect} in #{namespace.inspect} namespace." if has_namespace - raise UndefinedCommandError, "Could not find command #{command.inspect}." + raise UndefinedCommandError.new(command, all_commands.keys, (namespace if has_namespace)) end alias_method :handle_no_task_error, :handle_no_command_error diff --git a/lib/bundler/vendor/thor/lib/thor/error.rb b/lib/bundler/vendor/thor/lib/thor/error.rb index 2f816081f3..16c68294e4 100644 --- a/lib/bundler/vendor/thor/lib/thor/error.rb +++ b/lib/bundler/vendor/thor/lib/thor/error.rb @@ -1,4 +1,23 @@ class Bundler::Thor + Correctable = + begin + require 'did_you_mean' + + # In order to support versions of Ruby that don't have keyword + # arguments, we need our own spell checker class that doesn't take key + # words. Even though this code wouldn't be hit because of the check + # above, it's still necessary because the interpreter would otherwise be + # unable to parse the file. + class NoKwargSpellChecker < DidYouMean::SpellChecker # :nodoc: + def initialize(dictionary) + @dictionary = dictionary + end + end + + DidYouMean::Correctable + rescue LoadError, NameError + end + # Bundler::Thor::Error is raised when it's caused by wrong usage of thor classes. Those # errors have their backtrace suppressed and are nicely shown to the user. # @@ -10,6 +29,35 @@ class Bundler::Thor # Raised when a command was not found. class UndefinedCommandError < Error + class SpellChecker + attr_reader :error + + def initialize(error) + @error = error + end + + def corrections + @corrections ||= spell_checker.correct(error.command).map(&:inspect) + end + + def spell_checker + NoKwargSpellChecker.new(error.all_commands) + end + end + + attr_reader :command, :all_commands + + def initialize(command, all_commands, namespace) + @command = command + @all_commands = all_commands + + message = "Could not find command #{command.inspect}" + message = namespace ? "#{message} in #{namespace.inspect} namespace." : "#{message}." + + super(message) + end + + prepend Correctable if Correctable end UndefinedTaskError = UndefinedCommandError @@ -22,6 +70,33 @@ class Bundler::Thor end class UnknownArgumentError < Error + class SpellChecker + attr_reader :error + + def initialize(error) + @error = error + end + + def corrections + @corrections ||= + error.unknown.flat_map { |unknown| spell_checker.correct(unknown) }.uniq.map(&:inspect) + end + + def spell_checker + @spell_checker ||= NoKwargSpellChecker.new(error.switches) + end + end + + attr_reader :switches, :unknown + + def initialize(switches, unknown) + @switches = switches + @unknown = unknown + + super("Unknown switches #{unknown.map(&:inspect).join(', ')}") + end + + prepend Correctable if Correctable end class RequiredArgumentMissingError < InvocationError @@ -29,4 +104,11 @@ class Bundler::Thor class MalformattedArgumentError < InvocationError end + + if Correctable + DidYouMean::SPELL_CHECKERS.merge!( + 'Bundler::Thor::UndefinedCommandError' => UndefinedCommandError::SpellChecker, + 'Bundler::Thor::UnknownArgumentError' => UnknownArgumentError::SpellChecker + ) + end end diff --git a/lib/bundler/vendor/thor/lib/thor/group.rb b/lib/bundler/vendor/thor/lib/thor/group.rb index 05ddc10cd3..30db46529e 100644 --- a/lib/bundler/vendor/thor/lib/thor/group.rb +++ b/lib/bundler/vendor/thor/lib/thor/group.rb @@ -61,7 +61,7 @@ class Bundler::Thor::Group invocations[name] = false invocation_blocks[name] = block if block_given? - class_eval <<-METHOD, __FILE__, __LINE__ + class_eval <<-METHOD, __FILE__, __LINE__ + 1 def _invoke_#{name.to_s.gsub(/\W/, '_')} klass, command = self.class.prepare_for_invocation(nil, #{name.inspect}) @@ -120,7 +120,7 @@ class Bundler::Thor::Group invocations[name] = true invocation_blocks[name] = block if block_given? - class_eval <<-METHOD, __FILE__, __LINE__ + class_eval <<-METHOD, __FILE__, __LINE__ + 1 def _invoke_from_option_#{name.to_s.gsub(/\W/, '_')} return unless options[#{name.inspect}] diff --git a/lib/bundler/vendor/thor/lib/thor/parser/options.rb b/lib/bundler/vendor/thor/lib/thor/parser/options.rb index 70f6366842..179f4fa015 100644 --- a/lib/bundler/vendor/thor/lib/thor/parser/options.rb +++ b/lib/bundler/vendor/thor/lib/thor/parser/options.rb @@ -44,6 +44,7 @@ class Bundler::Thor @shorts = {} @switches = {} @extra = [] + @stopped_parsing_after_extra_index = nil options.each do |option| @switches[option.switch_name] = option @@ -66,6 +67,7 @@ class Bundler::Thor if result == OPTS_END shift @parsing_options = false + @stopped_parsing_after_extra_index ||= @extra.size super else result @@ -99,6 +101,7 @@ class Bundler::Thor elsif @stop_on_unknown @parsing_options = false @extra << shifted + @stopped_parsing_after_extra_index ||= @extra.size @extra << shift while peek break elsif match @@ -120,9 +123,11 @@ class Bundler::Thor end def check_unknown! + to_check = @stopped_parsing_after_extra_index ? @extra[0...@stopped_parsing_after_extra_index] : @extra + # an unknown option starts with - or -- and has no more --'s afterward. - unknown = @extra.select { |str| str =~ /^--?(?:(?!--).)*$/ } - raise UnknownArgumentError, "Unknown switches '#{unknown.join(', ')}'" unless unknown.empty? + unknown = to_check.select { |str| str =~ /^--?(?:(?!--).)*$/ } + raise UnknownArgumentError.new(@switches.keys, unknown) unless unknown.empty? end protected diff --git a/lib/bundler/vendor/thor/lib/thor/runner.rb b/lib/bundler/vendor/thor/lib/thor/runner.rb index b110b8d478..65ae422d7f 100644 --- a/lib/bundler/vendor/thor/lib/thor/runner.rb +++ b/lib/bundler/vendor/thor/lib/thor/runner.rb @@ -3,7 +3,7 @@ require "bundler/vendor/thor/lib/thor/group" require "bundler/vendor/thor/lib/thor/core_ext/io_binary_read" require "yaml" -require "digest" +require "digest/md5" require "pathname" class Bundler::Thor::Runner < Bundler::Thor #:nodoc: # rubocop:disable ClassLength @@ -90,7 +90,7 @@ class Bundler::Thor::Runner < Bundler::Thor #:nodoc: # rubocop:disable ClassLeng end thor_yaml[as] = { - :filename => Digest(:MD5).hexdigest(name + as), + :filename => Digest::MD5.hexdigest(name + as), :location => location, :namespaces => Bundler::Thor::Util.namespaces_in_content(contents, base) } diff --git a/lib/bundler/vendor/thor/lib/thor/shell.rb b/lib/bundler/vendor/thor/lib/thor/shell.rb index e945549324..a68cdf8a98 100644 --- a/lib/bundler/vendor/thor/lib/thor/shell.rb +++ b/lib/bundler/vendor/thor/lib/thor/shell.rb @@ -55,7 +55,7 @@ class Bundler::Thor # Common methods that are delegated to the shell. SHELL_DELEGATED_METHODS.each do |method| - module_eval <<-METHOD, __FILE__, __LINE__ + module_eval <<-METHOD, __FILE__, __LINE__ + 1 def #{method}(*args,&block) shell.#{method}(*args,&block) end diff --git a/lib/bundler/vendor/thor/lib/thor/shell/basic.rb b/lib/bundler/vendor/thor/lib/thor/shell/basic.rb index 5162390efd..52648fee8f 100644 --- a/lib/bundler/vendor/thor/lib/thor/shell/basic.rb +++ b/lib/bundler/vendor/thor/lib/thor/shell/basic.rb @@ -1,6 +1,8 @@ class Bundler::Thor module Shell class Basic + DEFAULT_TERMINAL_WIDTH = 80 + attr_accessor :base attr_reader :padding @@ -45,6 +47,10 @@ class Bundler::Thor # Asks something to the user and receives a response. # + # If a default value is specified it will be presented to the user + # and allows them to select that value with an empty response. This + # option is ignored when limited answers are supplied. + # # If asked to limit the correct responses, you can pass in an # array of acceptable answers. If one of those is not supplied, # they will be shown a message stating that one of those answers @@ -61,6 +67,8 @@ class Bundler::Thor # ==== Example # ask("What is your name?") # + # ask("What is the planet furthest from the sun?", :default => "Pluto") + # # ask("What is your favorite Neopolitan flavor?", :limited_to => ["strawberry", "chocolate", "vanilla"]) # # ask("What is your password?", :echo => false) @@ -222,8 +230,20 @@ class Bundler::Thor paras = message.split("\n\n") paras.map! do |unwrapped| - unwrapped.strip.tr("\n", " ").squeeze(" ").gsub(/.{1,#{width}}(?:\s|\Z)/) { ($& + 5.chr).gsub(/\n\005/, "\n").gsub(/\005/, "\n") } - end + counter = 0 + unwrapped.split(" ").inject do |memo, word| + word = word.gsub(/\n\005/, "\n").gsub(/\005/, "\n") + counter = 0 if word.include? "\n" + if (counter + word.length + 1) < width + memo = "#{memo} #{word}" + counter += (word.length + 1) + else + memo = "#{memo}\n#{word}" + counter = word.length + end + memo + end + end.compact! paras.each do |para| para.split("\n").each do |line| @@ -239,11 +259,11 @@ class Bundler::Thor # # ==== Parameters # destination<String>:: the destination file to solve conflicts - # block<Proc>:: an optional block that returns the value to be used in diff + # block<Proc>:: an optional block that returns the value to be used in diff and merge # def file_collision(destination) return true if @always_force - options = block_given? ? "[Ynaqdh]" : "[Ynaqh]" + options = block_given? ? "[Ynaqdhm]" : "[Ynaqh]" loop do answer = ask( @@ -267,6 +287,13 @@ class Bundler::Thor when is?(:diff) show_diff(destination, yield) if block_given? say "Retrying..." + when is?(:merge) + if block_given? && !merge_tool.empty? + merge(destination, yield) + return nil + end + + say "Please specify merge tool to `THOR_MERGE` env." else say file_collision_help end @@ -279,11 +306,11 @@ class Bundler::Thor result = if ENV["THOR_COLUMNS"] ENV["THOR_COLUMNS"].to_i else - unix? ? dynamic_width : 80 + unix? ? dynamic_width : DEFAULT_TERMINAL_WIDTH end - result < 10 ? 80 : result + result < 10 ? DEFAULT_TERMINAL_WIDTH : result rescue - 80 + DEFAULT_TERMINAL_WIDTH end # Called if something goes wrong during the execution. This is used by Bundler::Thor @@ -344,6 +371,7 @@ class Bundler::Thor q - quit, abort d - diff, show the differences between the old and the new h - help, show this help + m - merge, run merge tool HELP end @@ -432,6 +460,23 @@ class Bundler::Thor end correct_answer end + + def merge(destination, content) #:nodoc: + require "tempfile" + Tempfile.open([File.basename(destination), File.extname(destination)], File.dirname(destination)) do |temp| + temp.write content + temp.rewind + system %(#{merge_tool} "#{temp.path}" "#{destination}") + end + end + + def merge_tool #:nodoc: + @merge_tool ||= ENV["THOR_MERGE"] || git_merge_tool + end + + def git_merge_tool #:nodoc: + `git config merge.tool`.rstrip rescue "" + end end end end diff --git a/lib/bundler/vendor/thor/lib/thor/version.rb b/lib/bundler/vendor/thor/lib/thor/version.rb index df8f18821a..98f2b79081 100644 --- a/lib/bundler/vendor/thor/lib/thor/version.rb +++ b/lib/bundler/vendor/thor/lib/thor/version.rb @@ -1,3 +1,3 @@ class Bundler::Thor - VERSION = "0.20.0" + VERSION = "0.20.3" end |