bundler 2.4.21 → 2.4.22

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +21 -0
  3. data/README.md +1 -2
  4. data/lib/bundler/build_metadata.rb +2 -2
  5. data/lib/bundler/cli/gem.rb +3 -0
  6. data/lib/bundler/definition.rb +1 -1
  7. data/lib/bundler/endpoint_specification.rb +1 -1
  8. data/lib/bundler/errors.rb +15 -0
  9. data/lib/bundler/gem_helpers.rb +7 -0
  10. data/lib/bundler/installer/gem_installer.rb +5 -5
  11. data/lib/bundler/lazy_specification.rb +4 -0
  12. data/lib/bundler/plugin/index.rb +8 -0
  13. data/lib/bundler/plugin.rb +9 -2
  14. data/lib/bundler/rubygems_ext.rb +3 -4
  15. data/lib/bundler/rubygems_gem_installer.rb +23 -8
  16. data/lib/bundler/source/git/git_proxy.rb +9 -1
  17. data/lib/bundler/source/metadata.rb +1 -1
  18. data/lib/bundler/spec_set.rb +5 -2
  19. data/lib/bundler/templates/newgem/newgem.gemspec.tt +1 -1
  20. data/lib/bundler/ui/shell.rb +1 -1
  21. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/connection.rb +1 -0
  22. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/pool.rb +21 -9
  23. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb +1 -1
  24. data/lib/bundler/vendor/thor/lib/thor/actions/create_file.rb +3 -2
  25. data/lib/bundler/vendor/thor/lib/thor/actions/directory.rb +1 -1
  26. data/lib/bundler/vendor/thor/lib/thor/actions/empty_directory.rb +1 -1
  27. data/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb +8 -10
  28. data/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb +15 -4
  29. data/lib/bundler/vendor/thor/lib/thor/actions.rb +15 -15
  30. data/lib/bundler/vendor/thor/lib/thor/base.rb +140 -14
  31. data/lib/bundler/vendor/thor/lib/thor/command.rb +13 -4
  32. data/lib/bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb +4 -0
  33. data/lib/bundler/vendor/thor/lib/thor/error.rb +16 -25
  34. data/lib/bundler/vendor/thor/lib/thor/group.rb +1 -1
  35. data/lib/bundler/vendor/thor/lib/thor/invocation.rb +1 -1
  36. data/lib/bundler/vendor/thor/lib/thor/nested_context.rb +2 -2
  37. data/lib/bundler/vendor/thor/lib/thor/parser/argument.rb +20 -1
  38. data/lib/bundler/vendor/thor/lib/thor/parser/arguments.rb +33 -17
  39. data/lib/bundler/vendor/thor/lib/thor/parser/option.rb +27 -8
  40. data/lib/bundler/vendor/thor/lib/thor/parser/options.rb +44 -6
  41. data/lib/bundler/vendor/thor/lib/thor/rake_compat.rb +2 -2
  42. data/lib/bundler/vendor/thor/lib/thor/runner.rb +40 -30
  43. data/lib/bundler/vendor/thor/lib/thor/shell/basic.rb +26 -150
  44. data/lib/bundler/vendor/thor/lib/thor/shell/color.rb +4 -46
  45. data/lib/bundler/vendor/thor/lib/thor/shell/column_printer.rb +29 -0
  46. data/lib/bundler/vendor/thor/lib/thor/shell/html.rb +3 -45
  47. data/lib/bundler/vendor/thor/lib/thor/shell/lcs_diff.rb +49 -0
  48. data/lib/bundler/vendor/thor/lib/thor/shell/table_printer.rb +134 -0
  49. data/lib/bundler/vendor/thor/lib/thor/shell/terminal.rb +42 -0
  50. data/lib/bundler/vendor/thor/lib/thor/shell/wrapped_printer.rb +38 -0
  51. data/lib/bundler/vendor/thor/lib/thor/shell.rb +1 -1
  52. data/lib/bundler/vendor/thor/lib/thor/util.rb +8 -7
  53. data/lib/bundler/vendor/thor/lib/thor/version.rb +1 -1
  54. data/lib/bundler/vendor/thor/lib/thor.rb +155 -8
  55. data/lib/bundler/version.rb +1 -1
  56. data/lib/bundler/yaml_serializer.rb +6 -1
  57. data/lib/bundler.rb +0 -8
  58. metadata +8 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ce1668ecfd79ea439de5e81a89f9b06eed35f3b7d7309638071cb3b34f990d8c
4
- data.tar.gz: 24ef07902dda9ac445473bbe06f89fc5fcee012660198060978bc651bfd081bc
3
+ metadata.gz: 3f18e076341154326f22050b2bf57d7aa5f82278331f38079b4cb9ea53a13350
4
+ data.tar.gz: 5edcf31c166b6e65530b57c535c9e593a1cc24be7234a74b07eedbe448d775bf
5
5
  SHA512:
6
- metadata.gz: 14bca6c7a079404d2cb21e0f26a96277cc34df150e9c5b1696ffe819af944d1b1a079bd5a01292a9b0e0a35f0eb45be89298ccf2f18532acfba88adb80f413f6
7
- data.tar.gz: 3fc94ae1c36cec40fe55422e81db35d9b95e0bfcb103e8960bb80b38ad4aad569d8c626892ce62f8747491c68b560bc2bda851259f55a1293069bd888380376c
6
+ metadata.gz: 19f1201ce7d6a27379d5782373671a1fecfa6482bc699bdb89aa15ef9482f33659913874a5f22e8e3dece848350f411ebe96eb14f3fd3d6dc624455d85ba19de
7
+ data.tar.gz: 113401cf222bcf13bcda4424492b103f9bd9563c38f6dbdd7b7a7c4465e3971819a5ae77e1153987178d8353cf8d7aa79332484324bad00858a6f526b7f4a1b7
data/CHANGELOG.md CHANGED
@@ -1,3 +1,24 @@
1
+ # 2.4.22 (November 9, 2023)
2
+
3
+ ## Enhancements:
4
+
5
+ - Add Bundler::Plugin.loaded? helper [#6964](https://github.com/rubygems/rubygems/pull/6964)
6
+ - Give better error when previous installation folder is insecure to remove [#7030](https://github.com/rubygems/rubygems/pull/7030)
7
+ - Set file path when eval-ing local specification in EndpointSpecification [#7106](https://github.com/rubygems/rubygems/pull/7106)
8
+ - Git ignore the proper files for the CI service selected for `bundle gem` [#7101](https://github.com/rubygems/rubygems/pull/7101)
9
+ - Update vendored thor to v1.3.0 [#7078](https://github.com/rubygems/rubygems/pull/7078)
10
+ - Restore using old way of passing Ruby version to resolver [#7066](https://github.com/rubygems/rubygems/pull/7066)
11
+ - Bump vendored net-http-persistent to 4.0.2 [#6787](https://github.com/rubygems/rubygems/pull/6787)
12
+
13
+ ## Bug fixes:
14
+
15
+ - Fix regression when installing native extensions on universal rubies [#7077](https://github.com/rubygems/rubygems/pull/7077)
16
+ - Only remove bundler plugin gem when it's inside the cache [#7001](https://github.com/rubygems/rubygems/pull/7001)
17
+ - Don't show bug report template when GEM_HOME has no writable bit [#7113](https://github.com/rubygems/rubygems/pull/7113)
18
+ - Fix regression in old git versions [#7114](https://github.com/rubygems/rubygems/pull/7114)
19
+ - Handle empty array at built-in YAML serializer [#7099](https://github.com/rubygems/rubygems/pull/7099)
20
+ - Fix force_ruby_platform: when the lockfile only locks the ruby platform [#6936](https://github.com/rubygems/rubygems/pull/6936)
21
+
1
22
  # 2.4.21 (October 17, 2023)
2
23
 
3
24
  ## Enhancements:
data/README.md CHANGED
@@ -1,5 +1,4 @@
1
1
  [![Version ](https://img.shields.io/gem/v/bundler.svg?style=flat)](https://rubygems.org/gems/bundler)
2
- [![Slack ](https://bundler-slackin.herokuapp.com/badge.svg)](https://bundler-slackin.herokuapp.com)
3
2
 
4
3
  # Bundler: a gem to bundle gems
5
4
 
@@ -38,7 +37,7 @@ Still stuck? Try [filing an issue](https://github.com/rubygems/rubygems/issues/n
38
37
 
39
38
  To see what has changed in recent versions of Bundler, see the [CHANGELOG](CHANGELOG.md).
40
39
 
41
- To get in touch with the Bundler core team and other Bundler users, please join [the Bundler slack](https://slack.bundler.io).
40
+ To get in touch with the Bundler core team and other Bundler users, please join [the Bundler slack](https://join.slack.com/t/bundler/shared_invite/zt-1rrsuuv3m-OmXKWQf8K6iSla4~F1DBjQ).
42
41
 
43
42
  ### Contributing
44
43
 
@@ -4,8 +4,8 @@ module Bundler
4
4
  # Represents metadata from when the Bundler gem was built.
5
5
  module BuildMetadata
6
6
  # begin ivars
7
- @built_at = "2023-10-17".freeze
8
- @git_commit_sha = "d10b46bd15".freeze
7
+ @built_at = "2023-11-09".freeze
8
+ @git_commit_sha = "ec2089640".freeze
9
9
  @release = true
10
10
  # end ivars
11
11
 
@@ -137,10 +137,13 @@ module Bundler
137
137
  case config[:ci]
138
138
  when "github"
139
139
  templates.merge!("github/workflows/main.yml.tt" => ".github/workflows/main.yml")
140
+ config[:ci_config_path] = ".github "
140
141
  when "gitlab"
141
142
  templates.merge!("gitlab-ci.yml.tt" => ".gitlab-ci.yml")
143
+ config[:ci_config_path] = ".gitlab-ci.yml "
142
144
  when "circle"
143
145
  templates.merge!("circleci/config.yml.tt" => ".circleci/config.yml")
146
+ config[:ci_config_path] = ".circleci "
144
147
  end
145
148
 
146
149
  if ask_and_set(:mit, "Do you want to license your code permissively under the MIT license?",
@@ -881,7 +881,7 @@ module Bundler
881
881
 
882
882
  def metadata_dependencies
883
883
  @metadata_dependencies ||= [
884
- Dependency.new("Ruby\0", Gem.ruby_version),
884
+ Dependency.new("Ruby\0", Bundler::RubyVersion.system.gem_version),
885
885
  Dependency.new("RubyGems\0", Gem::VERSION),
886
886
  ]
887
887
  end
@@ -94,7 +94,7 @@ module Bundler
94
94
 
95
95
  def _local_specification
96
96
  return unless @loaded_from && File.exist?(local_specification_path)
97
- eval(File.read(local_specification_path)).tap do |spec|
97
+ eval(File.read(local_specification_path), nil, local_specification_path).tap do |spec|
98
98
  spec.loaded_from = @loaded_from
99
99
  end
100
100
  end
@@ -172,4 +172,19 @@ module Bundler
172
172
 
173
173
  status_code(36)
174
174
  end
175
+
176
+ class InsecureInstallPathError < BundlerError
177
+ def initialize(path)
178
+ @path = path
179
+ end
180
+
181
+ def message
182
+ "The installation path is insecure. Bundler cannot continue.\n" \
183
+ "#{@path} is world-writable (without sticky bit).\n" \
184
+ "Bundler cannot safely replace gems in world-writeable directories due to potential vulnerabilities.\n" \
185
+ "Please change the permissions of this directory or choose a different install path."
186
+ end
187
+
188
+ status_code(38)
189
+ end
175
190
  end
@@ -48,6 +48,13 @@ module Bundler
48
48
  end
49
49
  module_function :select_best_platform_match
50
50
 
51
+ def force_ruby_platform(specs)
52
+ matching = specs.select {|spec| spec.match_platform(Gem::Platform::RUBY) && spec.force_ruby_platform! }
53
+
54
+ sort_best_platform_match(matching, Gem::Platform::RUBY)
55
+ end
56
+ module_function :force_ruby_platform
57
+
51
58
  def sort_best_platform_match(matching, platform)
52
59
  exact = matching.select {|spec| spec.platform == platform }
53
60
  return exact if exact.any?
@@ -16,13 +16,13 @@ module Bundler
16
16
  post_install_message = install
17
17
  Bundler.ui.debug "#{worker}: #{spec.name} (#{spec.version}) from #{spec.loaded_from}"
18
18
  generate_executable_stubs
19
- return true, post_install_message
20
- rescue Bundler::InstallHookError, Bundler::SecurityError, Bundler::APIResponseMismatchError
19
+ [true, post_install_message]
20
+ rescue Bundler::InstallHookError, Bundler::SecurityError, Bundler::APIResponseMismatchError, Bundler::InsecureInstallPathError
21
21
  raise
22
22
  rescue Errno::ENOSPC
23
- return false, out_of_space_message
24
- rescue Bundler::BundlerError, Gem::InstallError, Bundler::APIResponseInvalidDependenciesError => e
25
- return false, specific_failure_message(e)
23
+ [false, out_of_space_message]
24
+ rescue Bundler::BundlerError, Gem::InstallError => e
25
+ [false, specific_failure_message(e)]
26
26
  end
27
27
 
28
28
  private
@@ -134,6 +134,10 @@ module Bundler
134
134
  " #{source.revision[0..6]}"
135
135
  end
136
136
 
137
+ def force_ruby_platform!
138
+ @force_ruby_platform = true
139
+ end
140
+
137
141
  private
138
142
 
139
143
  def use_exact_resolved_specifications?
@@ -136,6 +136,14 @@ module Bundler
136
136
  @hooks[event] || []
137
137
  end
138
138
 
139
+ # This plugin is installed inside the .bundle/plugin directory,
140
+ # and thus is managed solely by Bundler
141
+ def installed_in_plugin_root?(name)
142
+ return false unless (path = installed?(name))
143
+
144
+ path.start_with?("#{Plugin.root}/")
145
+ end
146
+
139
147
  private
140
148
 
141
149
  # Reads the index file from the directory and initializes the instance
@@ -62,7 +62,8 @@ module Bundler
62
62
  if names.any?
63
63
  names.each do |name|
64
64
  if index.installed?(name)
65
- Bundler.rm_rf(index.plugin_path(name))
65
+ path = index.plugin_path(name).to_s
66
+ Bundler.rm_rf(path) if index.installed_in_plugin_root?(name)
66
67
  index.unregister_plugin(name)
67
68
  Bundler.ui.info "Uninstalled plugin #{name}"
68
69
  else
@@ -227,7 +228,7 @@ module Bundler
227
228
  plugins = index.hook_plugins(event)
228
229
  return unless plugins.any?
229
230
 
230
- (plugins - @loaded_plugin_names).each {|name| load_plugin(name) }
231
+ plugins.each {|name| load_plugin(name) }
231
232
 
232
233
  @hooks_by_event[event].each {|blk| blk.call(*args, &arg_blk) }
233
234
  end
@@ -239,6 +240,11 @@ module Bundler
239
240
  Index.new.installed?(plugin)
240
241
  end
241
242
 
243
+ # @return [true, false] whether the plugin is loaded
244
+ def loaded?(plugin)
245
+ @loaded_plugin_names.include?(plugin)
246
+ end
247
+
242
248
  # Post installation processing and registering with index
243
249
  #
244
250
  # @param [Array<String>] plugins list to be installed
@@ -329,6 +335,7 @@ module Bundler
329
335
  # @param [String] name of the plugin
330
336
  def load_plugin(name)
331
337
  return unless name && !name.empty?
338
+ return if loaded?(name)
332
339
 
333
340
  # Need to ensure before this that plugin root where the rest of gems
334
341
  # are installed to be on load path to support plugin deps. Currently not
@@ -320,7 +320,7 @@ module Gem
320
320
  end
321
321
 
322
322
  # On universal Rubies, resolve the "universal" arch to the real CPU arch, without changing the extension directory.
323
- class Specification
323
+ class BasicSpecification
324
324
  if /^universal\.(?<arch>.*?)-/ =~ (CROSS_COMPILING || RUBY_PLATFORM)
325
325
  local_platform = Platform.local
326
326
  if local_platform.cpu == "universal"
@@ -333,9 +333,8 @@ module Gem
333
333
  end
334
334
 
335
335
  def extensions_dir
336
- Gem.default_ext_dir_for(base_dir) ||
337
- File.join(base_dir, "extensions", ORIGINAL_LOCAL_PLATFORM,
338
- Gem.extension_api_version)
336
+ @extensions_dir ||=
337
+ Gem.default_ext_dir_for(base_dir) || File.join(base_dir, "extensions", ORIGINAL_LOCAL_PLATFORM, Gem.extension_api_version)
339
338
  end
340
339
  end
341
340
  end
@@ -45,6 +45,14 @@ module Bundler
45
45
  spec
46
46
  end
47
47
 
48
+ def pre_install_checks
49
+ super && validate_bundler_checksum(options[:bundler_expected_checksum])
50
+ rescue Gem::FilePermissionError
51
+ # Ignore permission checks in RubyGems. Instead, go on, and try to write
52
+ # for real. We properly handle permission errors when they happen.
53
+ nil
54
+ end
55
+
48
56
  def generate_plugins
49
57
  return unless Gem::Installer.instance_methods(false).include?(:generate_plugins)
50
58
 
@@ -60,10 +68,6 @@ module Bundler
60
68
  end
61
69
  end
62
70
 
63
- def pre_install_checks
64
- super && validate_bundler_checksum(options[:bundler_expected_checksum])
65
- end
66
-
67
71
  def build_extensions
68
72
  extension_cache_path = options[:bundler_extension_cache_path]
69
73
  extension_dir = spec.extension_dir
@@ -108,11 +112,22 @@ module Bundler
108
112
  end
109
113
 
110
114
  def strict_rm_rf(dir)
111
- Bundler.rm_rf dir
112
- rescue StandardError => e
113
- raise unless File.exist?(dir)
115
+ return unless File.exist?(dir)
116
+
117
+ parent = File.dirname(dir)
118
+ parent_st = File.stat(parent)
119
+
120
+ if parent_st.world_writable? && !parent_st.sticky?
121
+ raise InsecureInstallPathError.new(parent)
122
+ end
123
+
124
+ begin
125
+ FileUtils.remove_entry_secure(dir)
126
+ rescue StandardError => e
127
+ raise unless File.exist?(dir)
114
128
 
115
- raise DirectoryRemovalError.new(e, "Could not delete previous installation of `#{dir}`")
129
+ raise DirectoryRemovalError.new(e, "Could not delete previous installation of `#{dir}`")
130
+ end
116
131
  end
117
132
 
118
133
  def validate_bundler_checksum(checksum)
@@ -131,7 +131,11 @@ module Bundler
131
131
  end
132
132
 
133
133
  ref = @commit_ref || (locked_to_full_sha? && @revision)
134
- git "fetch", "--force", "--quiet", *extra_fetch_args(ref), :dir => destination if ref
134
+ if ref
135
+ git "config", "uploadpack.allowAnySHA1InWant", "true", :dir => path.to_s if @commit_ref.nil? && needs_allow_any_sha1_in_want?
136
+
137
+ git "fetch", "--force", "--quiet", *extra_fetch_args(ref), :dir => destination
138
+ end
135
139
 
136
140
  git "reset", "--hard", @revision, :dir => destination
137
141
 
@@ -434,6 +438,10 @@ module Bundler
434
438
  @supports_minus_c ||= Gem::Version.new(version) >= Gem::Version.new("1.8.5")
435
439
  end
436
440
 
441
+ def needs_allow_any_sha1_in_want?
442
+ @needs_allow_any_sha1_in_want ||= Gem::Version.new(version) <= Gem::Version.new("2.13.7")
443
+ end
444
+
437
445
  def supports_fetching_unreachable_refs?
438
446
  @supports_fetching_unreachable_refs ||= Gem::Version.new(version) >= Gem::Version.new("2.5.0")
439
447
  end
@@ -5,7 +5,7 @@ module Bundler
5
5
  class Metadata < Source
6
6
  def specs
7
7
  @specs ||= Index.build do |idx|
8
- idx << Gem::Specification.new("Ruby\0", Gem.ruby_version)
8
+ idx << Gem::Specification.new("Ruby\0", Bundler::RubyVersion.system.gem_version)
9
9
  idx << Gem::Specification.new("RubyGems\0", Gem::VERSION) do |s|
10
10
  s.required_rubygems_version = Gem::Requirement.default
11
11
  end
@@ -200,8 +200,11 @@ module Bundler
200
200
 
201
201
  def specs_for_dependency(dep, platform)
202
202
  specs_for_name = lookup[dep.name]
203
- target_platform = dep.force_ruby_platform ? Gem::Platform::RUBY : (platform || Bundler.local_platform)
204
- matching_specs = GemHelpers.select_best_platform_match(specs_for_name, target_platform)
203
+ matching_specs = if dep.force_ruby_platform
204
+ GemHelpers.force_ruby_platform(specs_for_name)
205
+ else
206
+ GemHelpers.select_best_platform_match(specs_for_name, platform || Bundler.local_platform)
207
+ end
205
208
  matching_specs.map!(&:materialize_for_installation).compact! if platform.nil?
206
209
  matching_specs
207
210
  end
@@ -30,7 +30,7 @@ Gem::Specification.new do |spec|
30
30
  spec.files = Dir.chdir(__dir__) do
31
31
  `git ls-files -z`.split("\x0").reject do |f|
32
32
  (File.expand_path(f) == __FILE__) ||
33
- f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile])
33
+ f.start_with?(*%w[bin/ test/ spec/ features/ .git <%= config[:ci_config_path] %>appveyor Gemfile])
34
34
  end
35
35
  end
36
36
  spec.bindir = "exe"
@@ -147,7 +147,7 @@ module Bundler
147
147
  spaces ? text.gsub(/#{spaces}/, "") : text
148
148
  end
149
149
 
150
- def word_wrap(text, line_width = @shell.terminal_width)
150
+ def word_wrap(text, line_width = Thor::Terminal.terminal_width)
151
151
  strip_leading_spaces(text).split("\n").collect do |line|
152
152
  line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1\n").strip : line
153
153
  end * "\n"
@@ -25,6 +25,7 @@ class Bundler::Persistent::Net::HTTP::Persistent::Connection # :nodoc:
25
25
  ensure
26
26
  reset
27
27
  end
28
+ alias_method :close, :finish
28
29
 
29
30
  def reset
30
31
  @last_use = Bundler::Persistent::Net::HTTP::Persistent::EPOCH
@@ -11,20 +11,32 @@ class Bundler::Persistent::Net::HTTP::Persistent::Pool < Bundler::ConnectionPool
11
11
  end
12
12
 
13
13
  def checkin net_http_args
14
- stack = Thread.current[@key][net_http_args] ||= []
14
+ if net_http_args.is_a?(Hash) && net_http_args.size == 1 && net_http_args[:force]
15
+ # Bundler::ConnectionPool 2.4+ calls `checkin(force: true)` after fork.
16
+ # When this happens, we should remove all connections from Thread.current
17
+ if stacks = Thread.current[@key]
18
+ stacks.each do |http_args, connections|
19
+ connections.each do |conn|
20
+ @available.push conn, connection_args: http_args
21
+ end
22
+ connections.clear
23
+ end
24
+ end
25
+ else
26
+ stack = Thread.current[@key][net_http_args] ||= []
15
27
 
16
- raise Bundler::ConnectionPool::Error, 'no connections are checked out' if
17
- stack.empty?
28
+ raise Bundler::ConnectionPool::Error, 'no connections are checked out' if
29
+ stack.empty?
18
30
 
19
- conn = stack.pop
31
+ conn = stack.pop
20
32
 
21
- if stack.empty?
22
- @available.push conn, connection_args: net_http_args
33
+ if stack.empty?
34
+ @available.push conn, connection_args: net_http_args
23
35
 
24
- Thread.current[@key].delete(net_http_args)
25
- Thread.current[@key] = nil if Thread.current[@key].empty?
36
+ Thread.current[@key].delete(net_http_args)
37
+ Thread.current[@key] = nil if Thread.current[@key].empty?
38
+ end
26
39
  end
27
-
28
40
  nil
29
41
  end
30
42
 
@@ -174,7 +174,7 @@ class Bundler::Persistent::Net::HTTP::Persistent
174
174
  ##
175
175
  # The version of Bundler::Persistent::Net::HTTP::Persistent you are using
176
176
 
177
- VERSION = '4.0.1'
177
+ VERSION = '4.0.2'
178
178
 
179
179
  ##
180
180
  # Error class for errors raised by Bundler::Persistent::Net::HTTP::Persistent. Various
@@ -43,7 +43,8 @@ class Bundler::Thor
43
43
  # Boolean:: true if it is identical, false otherwise.
44
44
  #
45
45
  def identical?
46
- exists? && File.binread(destination) == render
46
+ # binread uses ASCII-8BIT, so to avoid false negatives, the string must use the same
47
+ exists? && File.binread(destination) == String.new(render).force_encoding("ASCII-8BIT")
47
48
  end
48
49
 
49
50
  # Holds the content to be added to the file.
@@ -60,7 +61,7 @@ class Bundler::Thor
60
61
  invoke_with_conflict_check do
61
62
  require "fileutils"
62
63
  FileUtils.mkdir_p(File.dirname(destination))
63
- File.open(destination, "wb") { |f| f.write render }
64
+ File.open(destination, "wb", config[:perm]) { |f| f.write render }
64
65
  end
65
66
  given_destination
66
67
  end
@@ -58,7 +58,7 @@ class Bundler::Thor
58
58
  def initialize(base, source, destination = nil, config = {}, &block)
59
59
  @source = File.expand_path(Dir[Util.escape_globs(base.find_in_source_paths(source.to_s))].first)
60
60
  @block = block
61
- super(base, destination, {:recursive => true}.merge(config))
61
+ super(base, destination, {recursive: true}.merge(config))
62
62
  end
63
63
 
64
64
  def invoke!
@@ -33,7 +33,7 @@ class Bundler::Thor
33
33
  #
34
34
  def initialize(base, destination, config = {})
35
35
  @base = base
36
- @config = {:verbose => true}.merge(config)
36
+ @config = {verbose: true}.merge(config)
37
37
  self.destination = destination
38
38
  end
39
39
 
@@ -66,12 +66,15 @@ class Bundler::Thor
66
66
  # ==== Parameters
67
67
  # source<String>:: the address of the given content.
68
68
  # destination<String>:: the relative path to the destination root.
69
- # config<Hash>:: give :verbose => false to not log the status.
69
+ # config<Hash>:: give :verbose => false to not log the status, and
70
+ # :http_headers => <Hash> to add headers to an http request.
70
71
  #
71
72
  # ==== Examples
72
73
  #
73
74
  # get "http://gist.github.com/103208", "doc/README"
74
75
  #
76
+ # get "http://gist.github.com/103208", "doc/README", :http_headers => {"Content-Type" => "application/json"}
77
+ #
75
78
  # get "http://gist.github.com/103208" do |content|
76
79
  # content.split("\n").first
77
80
  # end
@@ -82,10 +85,10 @@ class Bundler::Thor
82
85
 
83
86
  render = if source =~ %r{^https?\://}
84
87
  require "open-uri"
85
- URI.send(:open, source) { |input| input.binmode.read }
88
+ URI.send(:open, source, config.fetch(:http_headers, {})) { |input| input.binmode.read }
86
89
  else
87
90
  source = File.expand_path(find_in_source_paths(source.to_s))
88
- open(source) { |input| input.binmode.read }
91
+ File.open(source) { |input| input.binmode.read }
89
92
  end
90
93
 
91
94
  destination ||= if block_given?
@@ -120,12 +123,7 @@ class Bundler::Thor
120
123
  context = config.delete(:context) || instance_eval("binding")
121
124
 
122
125
  create_file destination, nil, config do
123
- match = ERB.version.match(/(\d+\.\d+\.\d+)/)
124
- capturable_erb = if match && match[1] >= "2.2.0" # Ruby 2.6+
125
- CapturableERB.new(::File.binread(source), :trim_mode => "-", :eoutvar => "@output_buffer")
126
- else
127
- CapturableERB.new(::File.binread(source), nil, "-", "@output_buffer")
128
- end
126
+ capturable_erb = CapturableERB.new(::File.binread(source), trim_mode: "-", eoutvar: "@output_buffer")
129
127
  content = capturable_erb.tap do |erb|
130
128
  erb.filename = source
131
129
  end.result(context)
@@ -252,7 +250,7 @@ class Bundler::Thor
252
250
  # flag<Regexp|String>:: the regexp or string to be replaced
253
251
  # replacement<String>:: the replacement, can be also given as a block
254
252
  # config<Hash>:: give :verbose => false to not log the status, and
255
- # :force => true, to force the replacement regardles of runner behavior.
253
+ # :force => true, to force the replacement regardless of runner behavior.
256
254
  #
257
255
  # ==== Example
258
256
  #
@@ -21,7 +21,7 @@ class Bundler::Thor
21
21
  # gems.split(" ").map{ |gem| " config.gem :#{gem}" }.join("\n")
22
22
  # end
23
23
  #
24
- WARNINGS = { unchanged_no_flag: 'File unchanged! The supplied flag value not found!' }
24
+ WARNINGS = {unchanged_no_flag: "File unchanged! Either the supplied flag value not found or the content has already been inserted!"}
25
25
 
26
26
  def insert_into_file(destination, *args, &block)
27
27
  data = block_given? ? block : args.shift
@@ -37,7 +37,7 @@ class Bundler::Thor
37
37
  attr_reader :replacement, :flag, :behavior
38
38
 
39
39
  def initialize(base, destination, data, config)
40
- super(base, destination, {:verbose => true}.merge(config))
40
+ super(base, destination, {verbose: true}.merge(config))
41
41
 
42
42
  @behavior, @flag = if @config.key?(:after)
43
43
  [:after, @config.delete(:after)]
@@ -59,6 +59,8 @@ class Bundler::Thor
59
59
  if exists?
60
60
  if replace!(/#{flag}/, content, config[:force])
61
61
  say_status(:invoke)
62
+ elsif replacement_present?
63
+ say_status(:unchanged, color: :blue)
62
64
  else
63
65
  say_status(:unchanged, warning: WARNINGS[:unchanged_no_flag], color: :red)
64
66
  end
@@ -96,6 +98,8 @@ class Bundler::Thor
96
98
  end
97
99
  elsif warning
98
100
  warning
101
+ elsif behavior == :unchanged
102
+ :unchanged
99
103
  else
100
104
  :subtract
101
105
  end
@@ -103,11 +107,18 @@ class Bundler::Thor
103
107
  super(status, (color || config[:verbose]))
104
108
  end
105
109
 
110
+ def content
111
+ @content ||= File.read(destination)
112
+ end
113
+
114
+ def replacement_present?
115
+ content.include?(replacement)
116
+ end
117
+
106
118
  # Adds the content to the file.
107
119
  #
108
120
  def replace!(regexp, string, force)
109
- content = File.read(destination)
110
- if force || !content.include?(replacement)
121
+ if force || !replacement_present?
111
122
  success = content.gsub!(regexp, string)
112
123
 
113
124
  File.open(destination, "wb") { |file| file.write(content) } unless pretend?