bundler 2.5.5 → 2.5.16

Sign up to get free protection for your applications and to get access to all the features.
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