bundler 2.5.5 → 2.5.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +171 -0
  3. data/lib/bundler/build_metadata.rb +2 -2
  4. data/lib/bundler/cli/binstubs.rb +1 -1
  5. data/lib/bundler/cli/fund.rb +1 -1
  6. data/lib/bundler/cli/gem.rb +8 -15
  7. data/lib/bundler/cli/install.rb +1 -1
  8. data/lib/bundler/cli/lock.rb +5 -4
  9. data/lib/bundler/cli/plugin.rb +3 -2
  10. data/lib/bundler/cli.rb +14 -31
  11. data/lib/bundler/compact_index_client/cache.rb +47 -72
  12. data/lib/bundler/compact_index_client/parser.rb +84 -0
  13. data/lib/bundler/compact_index_client.rb +51 -80
  14. data/lib/bundler/constants.rb +8 -1
  15. data/lib/bundler/definition.rb +168 -99
  16. data/lib/bundler/dependency.rb +2 -1
  17. data/lib/bundler/dsl.rb +16 -1
  18. data/lib/bundler/endpoint_specification.rb +11 -0
  19. data/lib/bundler/env.rb +1 -1
  20. data/lib/bundler/environment_preserver.rb +5 -23
  21. data/lib/bundler/errors.rb +14 -0
  22. data/lib/bundler/fetcher/compact_index.rb +15 -24
  23. data/lib/bundler/fetcher/downloader.rb +1 -1
  24. data/lib/bundler/fetcher.rb +2 -2
  25. data/lib/bundler/gem_helper.rb +1 -1
  26. data/lib/bundler/gem_helpers.rb +14 -7
  27. data/lib/bundler/gem_version_promoter.rb +42 -38
  28. data/lib/bundler/injector.rb +4 -6
  29. data/lib/bundler/installer/gem_installer.rb +0 -1
  30. data/lib/bundler/installer/standalone.rb +0 -3
  31. data/lib/bundler/installer.rb +11 -13
  32. data/lib/bundler/lazy_specification.rb +1 -0
  33. data/lib/bundler/man/bundle-add.1 +1 -1
  34. data/lib/bundler/man/bundle-binstubs.1 +1 -1
  35. data/lib/bundler/man/bundle-cache.1 +1 -1
  36. data/lib/bundler/man/bundle-check.1 +3 -1
  37. data/lib/bundler/man/bundle-check.1.ronn +3 -0
  38. data/lib/bundler/man/bundle-clean.1 +1 -1
  39. data/lib/bundler/man/bundle-config.1 +2 -4
  40. data/lib/bundler/man/bundle-config.1.ronn +1 -4
  41. data/lib/bundler/man/bundle-console.1 +1 -1
  42. data/lib/bundler/man/bundle-doctor.1 +1 -1
  43. data/lib/bundler/man/bundle-exec.1 +1 -1
  44. data/lib/bundler/man/bundle-gem.1 +7 -1
  45. data/lib/bundler/man/bundle-gem.1.ronn +11 -0
  46. data/lib/bundler/man/bundle-help.1 +1 -1
  47. data/lib/bundler/man/bundle-info.1 +1 -1
  48. data/lib/bundler/man/bundle-init.1 +1 -1
  49. data/lib/bundler/man/bundle-inject.1 +1 -1
  50. data/lib/bundler/man/bundle-install.1 +3 -3
  51. data/lib/bundler/man/bundle-install.1.ronn +2 -2
  52. data/lib/bundler/man/bundle-list.1 +1 -1
  53. data/lib/bundler/man/bundle-lock.1 +1 -1
  54. data/lib/bundler/man/bundle-open.1 +1 -1
  55. data/lib/bundler/man/bundle-outdated.1 +1 -1
  56. data/lib/bundler/man/bundle-platform.1 +1 -1
  57. data/lib/bundler/man/bundle-plugin.1 +7 -4
  58. data/lib/bundler/man/bundle-plugin.1.ronn +7 -3
  59. data/lib/bundler/man/bundle-pristine.1 +1 -1
  60. data/lib/bundler/man/bundle-remove.1 +1 -1
  61. data/lib/bundler/man/bundle-show.1 +1 -1
  62. data/lib/bundler/man/bundle-update.1 +1 -1
  63. data/lib/bundler/man/bundle-version.1 +1 -1
  64. data/lib/bundler/man/bundle-viz.1 +1 -1
  65. data/lib/bundler/man/bundle.1 +1 -1
  66. data/lib/bundler/man/gemfile.5 +3 -3
  67. data/lib/bundler/man/gemfile.5.ronn +2 -2
  68. data/lib/bundler/mirror.rb +3 -3
  69. data/lib/bundler/plugin/api/source.rb +2 -2
  70. data/lib/bundler/plugin/installer/path.rb +18 -0
  71. data/lib/bundler/plugin/installer.rb +36 -16
  72. data/lib/bundler/plugin/source_list.rb +4 -4
  73. data/lib/bundler/resolver/base.rb +4 -0
  74. data/lib/bundler/resolver/candidate.rb +5 -17
  75. data/lib/bundler/resolver/package.rb +4 -0
  76. data/lib/bundler/resolver/spec_group.rb +20 -2
  77. data/lib/bundler/resolver.rb +72 -33
  78. data/lib/bundler/rubygems_ext.rb +98 -10
  79. data/lib/bundler/rubygems_gem_installer.rb +35 -2
  80. data/lib/bundler/rubygems_integration.rb +16 -2
  81. data/lib/bundler/runtime.rb +2 -2
  82. data/lib/bundler/self_manager.rb +22 -2
  83. data/lib/bundler/settings.rb +26 -20
  84. data/lib/bundler/setup.rb +6 -0
  85. data/lib/bundler/shared_helpers.rb +6 -4
  86. data/lib/bundler/source/git/git_proxy.rb +9 -1
  87. data/lib/bundler/source/git.rb +15 -1
  88. data/lib/bundler/source/metadata.rb +2 -0
  89. data/lib/bundler/source/path.rb +0 -13
  90. data/lib/bundler/source/rubygems/remote.rb +1 -1
  91. data/lib/bundler/source/rubygems.rb +33 -32
  92. data/lib/bundler/source_list.rb +26 -2
  93. data/lib/bundler/spec_set.rb +15 -13
  94. data/lib/bundler/stub_specification.rb +8 -0
  95. data/lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt +77 -29
  96. data/lib/bundler/templates/newgem/newgem.gemspec.tt +4 -3
  97. data/lib/bundler/templates/newgem/rubocop.yml.tt +0 -5
  98. data/lib/bundler/uri_credentials_filter.rb +2 -2
  99. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb +22 -22
  100. data/lib/bundler/vendor/pub_grub/lib/pub_grub/static_package_source.rb +1 -0
  101. data/lib/bundler/vendored_net_http.rb +20 -5
  102. data/lib/bundler/vendored_timeout.rb +7 -3
  103. data/lib/bundler/vendored_uri.rb +18 -1
  104. data/lib/bundler/version.rb +1 -1
  105. data/lib/bundler/yaml_serializer.rb +11 -6
  106. data/lib/bundler.rb +29 -3
  107. metadata +5 -3
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bundler
4
+ class CompactIndexClient
5
+ class Parser
6
+ # `compact_index` - an object responding to #names, #versions, #info(name, checksum),
7
+ # returning the file contents as a string
8
+ def initialize(compact_index)
9
+ @compact_index = compact_index
10
+ @info_checksums = nil
11
+ @versions_by_name = nil
12
+ @available = nil
13
+ @gem_parser = nil
14
+ end
15
+
16
+ def names
17
+ lines(@compact_index.names)
18
+ end
19
+
20
+ def versions
21
+ @versions_by_name ||= Hash.new {|hash, key| hash[key] = [] }
22
+ @info_checksums = {}
23
+
24
+ lines(@compact_index.versions).each do |line|
25
+ name, versions_string, checksum = line.split(" ", 3)
26
+ @info_checksums[name] = checksum || ""
27
+ versions_string.split(",") do |version|
28
+ delete = version.delete_prefix!("-")
29
+ version = version.split("-", 2).unshift(name)
30
+ if delete
31
+ @versions_by_name[name].delete(version)
32
+ else
33
+ @versions_by_name[name] << version
34
+ end
35
+ end
36
+ end
37
+
38
+ @versions_by_name
39
+ end
40
+
41
+ def info(name)
42
+ data = @compact_index.info(name, info_checksums[name])
43
+ lines(data).map {|line| gem_parser.parse(line).unshift(name) }
44
+ end
45
+
46
+ def available?
47
+ return @available unless @available.nil?
48
+ @available = !info_checksums.empty?
49
+ end
50
+
51
+ private
52
+
53
+ def info_checksums
54
+ @info_checksums ||= lines(@compact_index.versions).each_with_object({}) do |line, checksums|
55
+ parse_version_checksum(line, checksums)
56
+ end
57
+ end
58
+
59
+ def lines(data)
60
+ return [] if data.nil? || data.empty?
61
+ lines = data.split("\n")
62
+ header = lines.index("---")
63
+ header ? lines[header + 1..-1] : lines
64
+ end
65
+
66
+ def gem_parser
67
+ @gem_parser ||= GemParser.new
68
+ end
69
+
70
+ # This is mostly the same as `split(" ", 3)` but it avoids allocating extra objects.
71
+ # This method gets called at least once for every gem when parsing versions.
72
+ def parse_version_checksum(line, checksums)
73
+ return unless (name_end = line.index(" ")) # Artifactory bug causes blank lines in artifactor index files
74
+ return unless (checksum_start = line.index(" ", name_end + 1) + 1)
75
+ checksum_end = line.size - checksum_start
76
+
77
+ line.freeze # allows slicing into the string to not allocate a copy of the line
78
+ name = line[0, name_end]
79
+ checksum = line[checksum_start, checksum_end]
80
+ checksums[name.freeze] = checksum # freeze name since it is used as a hash key
81
+ end
82
+ end
83
+ end
84
+ end
@@ -4,6 +4,29 @@ require "pathname"
4
4
  require "set"
5
5
 
6
6
  module Bundler
7
+ # The CompactIndexClient is responsible for fetching and parsing the compact index.
8
+ #
9
+ # The compact index is a set of caching optimized files that are used to fetch gem information.
10
+ # The files are:
11
+ # - names: a list of all gem names
12
+ # - versions: a list of all gem versions
13
+ # - info/[gem]: a list of all versions of a gem
14
+ #
15
+ # The client is instantiated with:
16
+ # - `directory`: the root directory where the cache files are stored.
17
+ # - `fetcher`: (optional) an object that responds to #call(uri_path, headers) and returns an http response.
18
+ # If the `fetcher` is not provided, the client will only read cached files from disk.
19
+ #
20
+ # The client is organized into:
21
+ # - `Updater`: updates the cached files on disk using the fetcher.
22
+ # - `Cache`: calls the updater, caches files, read and return them from disk
23
+ # - `Parser`: parses the compact index file data
24
+ # - `CacheFile`: a concurrency safe file reader/writer that verifies checksums
25
+ #
26
+ # The client is intended to optimize memory usage and performance.
27
+ # It is called 100s or 1000s of times, parsing files with hundreds of thousands of lines.
28
+ # It may be called concurrently without global interpreter lock in some Rubies.
29
+ # As a result, some methods may look more complex than necessary to save memory or time.
7
30
  class CompactIndexClient
8
31
  # NOTE: MD5 is here not because we expect a server to respond with it, but
9
32
  # because we use it to generate the etag on first request during the upgrade
@@ -12,6 +35,13 @@ module Bundler
12
35
  SUPPORTED_DIGESTS = { "sha-256" => :SHA256, "md5" => :MD5 }.freeze
13
36
  DEBUG_MUTEX = Thread::Mutex.new
14
37
 
38
+ # info returns an Array of INFO Arrays. Each INFO Array has the following indices:
39
+ INFO_NAME = 0
40
+ INFO_VERSION = 1
41
+ INFO_PLATFORM = 2
42
+ INFO_DEPS = 3
43
+ INFO_REQS = 4
44
+
15
45
  def self.debug
16
46
  return unless ENV["DEBUG_COMPACT_INDEX"]
17
47
  DEBUG_MUTEX.synchronize { warn("[#{self}] #{yield}") }
@@ -21,106 +51,47 @@ module Bundler
21
51
 
22
52
  require_relative "compact_index_client/cache"
23
53
  require_relative "compact_index_client/cache_file"
54
+ require_relative "compact_index_client/parser"
24
55
  require_relative "compact_index_client/updater"
25
56
 
26
- attr_reader :directory
27
-
28
- def initialize(directory, fetcher)
29
- @directory = Pathname.new(directory)
30
- @updater = Updater.new(fetcher)
31
- @cache = Cache.new(@directory)
32
- @endpoints = Set.new
33
- @info_checksums_by_name = {}
34
- @parsed_checksums = false
35
- @mutex = Thread::Mutex.new
36
- end
37
-
38
- def execution_mode=(block)
39
- Bundler::CompactIndexClient.debug { "execution_mode=" }
40
- @endpoints = Set.new
41
-
42
- @execution_mode = block
43
- end
44
-
45
- # @return [Lambda] A lambda that takes an array of inputs and a block, and
46
- # maps the inputs with the block in parallel.
47
- #
48
- def execution_mode
49
- @execution_mode || sequentially
50
- end
51
-
52
- def sequential_execution_mode!
53
- self.execution_mode = sequentially
54
- end
55
-
56
- def sequentially
57
- @sequentially ||= lambda do |inputs, &blk|
58
- inputs.map(&blk)
59
- end
57
+ def initialize(directory, fetcher = nil)
58
+ @cache = Cache.new(directory, fetcher)
59
+ @parser = Parser.new(@cache)
60
60
  end
61
61
 
62
62
  def names
63
- Bundler::CompactIndexClient.debug { "/names" }
64
- update("names", @cache.names_path, @cache.names_etag_path)
65
- @cache.names
63
+ Bundler::CompactIndexClient.debug { "names" }
64
+ @parser.names
66
65
  end
67
66
 
68
67
  def versions
69
- Bundler::CompactIndexClient.debug { "/versions" }
70
- update("versions", @cache.versions_path, @cache.versions_etag_path)
71
- versions, @info_checksums_by_name = @cache.versions
72
- versions
68
+ Bundler::CompactIndexClient.debug { "versions" }
69
+ @parser.versions
73
70
  end
74
71
 
75
72
  def dependencies(names)
76
73
  Bundler::CompactIndexClient.debug { "dependencies(#{names})" }
77
- execution_mode.call(names) do |name|
78
- update_info(name)
79
- @cache.dependencies(name).map {|d| d.unshift(name) }
80
- end.flatten(1)
74
+ names.map {|name| info(name) }
81
75
  end
82
76
 
83
- def update_and_parse_checksums!
84
- Bundler::CompactIndexClient.debug { "update_and_parse_checksums!" }
85
- return @info_checksums_by_name if @parsed_checksums
86
- update("versions", @cache.versions_path, @cache.versions_etag_path)
87
- @info_checksums_by_name = @cache.checksums
88
- @parsed_checksums = true
89
- end
90
-
91
- private
92
-
93
- def update(remote_path, local_path, local_etag_path)
94
- Bundler::CompactIndexClient.debug { "update(#{local_path}, #{remote_path})" }
95
- unless synchronize { @endpoints.add?(remote_path) }
96
- Bundler::CompactIndexClient.debug { "already fetched #{remote_path}" }
97
- return
98
- end
99
- @updater.update(url(remote_path), local_path, local_etag_path)
77
+ def info(name)
78
+ Bundler::CompactIndexClient.debug { "info(#{name})" }
79
+ @parser.info(name)
100
80
  end
101
81
 
102
- def update_info(name)
103
- Bundler::CompactIndexClient.debug { "update_info(#{name})" }
104
- path = @cache.info_path(name)
105
- unless existing = @info_checksums_by_name[name]
106
- Bundler::CompactIndexClient.debug { "skipping updating info for #{name} since it is missing from versions" }
107
- return
108
- end
109
- checksum = SharedHelpers.checksum_for_file(path, :MD5)
110
- if checksum == existing
111
- Bundler::CompactIndexClient.debug { "skipping updating info for #{name} since the versions checksum matches the local checksum" }
112
- return
113
- end
114
- Bundler::CompactIndexClient.debug { "updating info for #{name} since the versions checksum #{existing} != the local checksum #{checksum}" }
115
- update("info/#{name}", path, @cache.info_etag_path(name))
82
+ def latest_version(name)
83
+ Bundler::CompactIndexClient.debug { "latest_version(#{name})" }
84
+ @parser.info(name).map {|d| Gem::Version.new(d[INFO_VERSION]) }.max
116
85
  end
117
86
 
118
- def url(path)
119
- path
87
+ def available?
88
+ Bundler::CompactIndexClient.debug { "available?" }
89
+ @parser.available?
120
90
  end
121
91
 
122
- def synchronize
123
- @mutex.synchronize { yield }
92
+ def reset!
93
+ Bundler::CompactIndexClient.debug { "reset!" }
94
+ @cache.reset!
124
95
  end
125
96
  end
126
97
  end
@@ -1,7 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "rbconfig"
4
+
3
5
  module Bundler
4
6
  WINDOWS = RbConfig::CONFIG["host_os"] =~ /(msdos|mswin|djgpp|mingw)/
7
+ deprecate_constant :WINDOWS
8
+
5
9
  FREEBSD = RbConfig::CONFIG["host_os"].to_s.include?("bsd")
6
- NULL = File::NULL
10
+ deprecate_constant :FREEBSD
11
+
12
+ NULL = File::NULL
13
+ deprecate_constant :NULL
7
14
  end