rubygems-update 2.6.7 → 2.6.8
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rubygems-update might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/History.txt +11 -0
- data/Manifest.txt +10 -5
- data/bundler/CHANGELOG.md +108 -0
- data/bundler/DEVELOPMENT.md +6 -4
- data/bundler/ISSUES.md +17 -0
- data/bundler/README.md +2 -0
- data/bundler/exe/bundle +4 -6
- data/bundler/exe/bundle_ruby +2 -4
- data/bundler/exe/bundler +1 -19
- data/bundler/lib/bundler.rb +114 -44
- data/bundler/lib/bundler/cli.rb +90 -17
- data/bundler/lib/bundler/cli/binstubs.rb +4 -3
- data/bundler/lib/bundler/cli/cache.rb +1 -1
- data/bundler/lib/bundler/cli/check.rb +1 -1
- data/bundler/lib/bundler/cli/clean.rb +1 -1
- data/bundler/lib/bundler/cli/common.rb +13 -0
- data/bundler/lib/bundler/cli/console.rb +3 -0
- data/bundler/lib/bundler/cli/doctor.rb +93 -0
- data/bundler/lib/bundler/cli/exec.rb +18 -2
- data/bundler/lib/bundler/cli/gem.rb +3 -2
- data/bundler/lib/bundler/cli/inject.rb +25 -7
- data/bundler/lib/bundler/cli/install.rb +25 -7
- data/bundler/lib/bundler/cli/lock.rb +20 -7
- data/bundler/lib/bundler/cli/outdated.rb +97 -38
- data/bundler/lib/bundler/cli/platform.rb +1 -1
- data/bundler/lib/bundler/cli/show.rb +1 -1
- data/bundler/lib/bundler/cli/update.rb +9 -6
- data/bundler/lib/bundler/compact_index_client.rb +102 -0
- data/bundler/lib/bundler/compact_index_client/cache.rb +119 -0
- data/bundler/lib/bundler/compact_index_client/updater.rb +88 -0
- data/bundler/lib/bundler/current_ruby.rb +3 -3
- data/bundler/lib/bundler/definition.rb +210 -46
- data/bundler/lib/bundler/dependency.rb +1 -1
- data/bundler/lib/bundler/deployment.rb +6 -0
- data/bundler/lib/bundler/deprecate.rb +16 -0
- data/bundler/lib/bundler/dsl.rb +70 -24
- data/bundler/lib/bundler/endpoint_specification.rb +2 -0
- data/bundler/lib/bundler/env.rb +5 -1
- data/bundler/lib/bundler/environment_preserver.rb +1 -1
- data/bundler/lib/bundler/errors.rb +12 -1
- data/bundler/lib/bundler/feature_flag.rb +32 -0
- data/bundler/lib/bundler/fetcher.rb +3 -2
- data/bundler/lib/bundler/fetcher/base.rb +10 -0
- data/bundler/lib/bundler/fetcher/compact_index.rb +33 -12
- data/bundler/lib/bundler/fetcher/dependency.rb +2 -13
- data/bundler/lib/bundler/fetcher/downloader.rb +12 -1
- data/bundler/lib/bundler/friendly_errors.rb +9 -2
- data/bundler/lib/bundler/gem_helper.rb +3 -3
- data/bundler/lib/bundler/gem_helpers.rb +69 -1
- data/bundler/lib/bundler/gem_version_promoter.rb +175 -0
- data/bundler/lib/bundler/gemdeps.rb +28 -0
- data/bundler/lib/bundler/graph.rb +4 -25
- data/bundler/lib/bundler/index.rb +11 -2
- data/bundler/lib/bundler/injector.rb +12 -5
- data/bundler/lib/bundler/inline.rb +4 -4
- data/bundler/lib/bundler/installer.rb +25 -9
- data/bundler/lib/bundler/installer/gem_installer.rb +13 -15
- data/bundler/lib/bundler/installer/parallel_installer.rb +121 -99
- data/bundler/lib/bundler/lazy_specification.rb +28 -3
- data/bundler/lib/bundler/lockfile_parser.rb +27 -17
- data/bundler/lib/bundler/match_platform.rb +2 -1
- data/bundler/lib/bundler/mirror.rb +2 -2
- data/bundler/lib/bundler/plugin.rb +156 -32
- data/bundler/lib/bundler/plugin/api.rb +29 -5
- data/bundler/lib/bundler/plugin/api/source.rb +293 -0
- data/bundler/lib/bundler/plugin/dsl.rb +25 -1
- data/bundler/lib/bundler/plugin/index.rb +80 -13
- data/bundler/lib/bundler/plugin/installer.rb +6 -10
- data/bundler/lib/bundler/plugin/source_list.rb +4 -0
- data/bundler/lib/bundler/postit_trampoline.rb +56 -40
- data/bundler/lib/bundler/remote_specification.rb +5 -0
- data/bundler/lib/bundler/resolver.rb +64 -47
- data/bundler/lib/bundler/retry.rb +2 -1
- data/bundler/lib/bundler/ruby_version.rb +11 -4
- data/bundler/lib/bundler/rubygems_ext.rb +25 -3
- data/bundler/lib/bundler/rubygems_gem_installer.rb +54 -0
- data/bundler/lib/bundler/rubygems_integration.rb +148 -70
- data/bundler/lib/bundler/runtime.rb +27 -3
- data/bundler/lib/bundler/settings.rb +80 -17
- data/bundler/lib/bundler/setup.rb +7 -4
- data/bundler/lib/bundler/shared_helpers.rb +45 -8
- data/bundler/lib/bundler/source.rb +2 -1
- data/bundler/lib/bundler/source/gemspec.rb +4 -0
- data/bundler/lib/bundler/source/git.rb +9 -6
- data/bundler/lib/bundler/source/git/git_proxy.rb +37 -4
- data/bundler/lib/bundler/source/path.rb +10 -27
- data/bundler/lib/bundler/source/path/installer.rb +39 -11
- data/bundler/lib/bundler/source/rubygems.rb +3 -2
- data/bundler/lib/bundler/source_list.rb +28 -8
- data/bundler/lib/bundler/spec_set.rb +30 -15
- data/bundler/lib/bundler/templates/Executable.standalone +4 -2
- data/bundler/lib/bundler/templates/Gemfile +0 -1
- data/bundler/lib/bundler/templates/newgem/README.md.tt +1 -1
- data/bundler/lib/bundler/templates/newgem/bin/console.tt +1 -1
- data/bundler/lib/bundler/ui/shell.rb +25 -9
- data/bundler/lib/bundler/ui/silent.rb +10 -0
- data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb +1 -1
- data/bundler/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb +8 -2
- data/bundler/lib/bundler/vendor/postit/lib/postit.rb +5 -5
- data/bundler/lib/bundler/vendor/postit/lib/postit/environment.rb +3 -3
- data/bundler/lib/bundler/vendor/postit/lib/postit/installer.rb +1 -1
- data/bundler/lib/bundler/vendor/postit/lib/postit/parser.rb +1 -1
- data/bundler/lib/bundler/vendor/postit/lib/postit/setup.rb +4 -4
- data/bundler/lib/bundler/vendor/postit/lib/postit/version.rb +2 -2
- data/bundler/lib/bundler/version.rb +1 -1
- data/bundler/lib/bundler/yaml_serializer.rb +34 -11
- data/bundler/man/bundle-binstubs.ronn +29 -0
- data/bundler/man/bundle-config.ronn +33 -1
- data/bundler/man/bundle-exec.ronn +9 -0
- data/bundler/man/bundle-install.ronn +6 -41
- data/bundler/man/bundle-package.ronn +1 -1
- data/bundler/man/bundle.ronn +9 -8
- data/bundler/man/gemfile.5.ronn +1 -1
- data/lib/rubygems.rb +1 -1
- data/lib/rubygems/dependency.rb +7 -4
- data/lib/rubygems/request.rb +46 -0
- data/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb +7 -0
- data/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action.rb +1 -1
- data/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb +2 -2
- data/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_vertex.rb +2 -2
- data/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/delete_edge.rb +62 -0
- data/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb +1 -1
- data/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/log.rb +12 -1
- data/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/set_payload.rb +2 -2
- data/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/tag.rb +2 -2
- data/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb +1 -1
- data/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb +11 -3
- data/test/rubygems/test_gem_request.rb +132 -0
- data/test/rubygems/test_gem_specification.rb +7 -0
- metadata +34 -29
- data/bundler/lib/bundler/environment.rb +0 -42
- data/bundler/lib/bundler/vendor/compact_index_client/lib/compact_index_client.rb +0 -79
- data/bundler/lib/bundler/vendor/compact_index_client/lib/compact_index_client/cache.rb +0 -98
- data/bundler/lib/bundler/vendor/compact_index_client/lib/compact_index_client/updater.rb +0 -80
- data/bundler/lib/bundler/vendor/compact_index_client/lib/compact_index_client/version.rb +0 -4
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "pathname"
|
3
|
+
require "set"
|
4
|
+
|
5
|
+
module Bundler
|
6
|
+
class CompactIndexClient
|
7
|
+
DEBUG_MUTEX = Mutex.new
|
8
|
+
def self.debug
|
9
|
+
return unless ENV["DEBUG_COMPACT_INDEX"]
|
10
|
+
DEBUG_MUTEX.synchronize { warn("[#{self}] #{yield}") }
|
11
|
+
end
|
12
|
+
|
13
|
+
class Error < StandardError; end
|
14
|
+
|
15
|
+
require "bundler/compact_index_client/cache"
|
16
|
+
require "bundler/compact_index_client/updater"
|
17
|
+
|
18
|
+
attr_reader :directory
|
19
|
+
|
20
|
+
# @return [Lambda] A lambda that takes an array of inputs and a block, and
|
21
|
+
# maps the inputs with the block in parallel.
|
22
|
+
#
|
23
|
+
attr_accessor :in_parallel
|
24
|
+
|
25
|
+
def initialize(directory, fetcher)
|
26
|
+
@directory = Pathname.new(directory)
|
27
|
+
@updater = Updater.new(fetcher)
|
28
|
+
@cache = Cache.new(@directory)
|
29
|
+
@endpoints = Set.new
|
30
|
+
@info_checksums_by_name = {}
|
31
|
+
@in_parallel = lambda do |inputs, &blk|
|
32
|
+
inputs.map(&blk)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def names
|
37
|
+
Bundler::CompactIndexClient.debug { "/names" }
|
38
|
+
update(@cache.names_path, "names")
|
39
|
+
@cache.names
|
40
|
+
end
|
41
|
+
|
42
|
+
def versions
|
43
|
+
Bundler::CompactIndexClient.debug { "/versions" }
|
44
|
+
update(@cache.versions_path, "versions")
|
45
|
+
versions, @info_checksums_by_name = @cache.versions
|
46
|
+
versions
|
47
|
+
end
|
48
|
+
|
49
|
+
def dependencies(names)
|
50
|
+
Bundler::CompactIndexClient.debug { "dependencies(#{names})" }
|
51
|
+
in_parallel.call(names) do |name|
|
52
|
+
update_info(name)
|
53
|
+
@cache.dependencies(name).map {|d| d.unshift(name) }
|
54
|
+
end.flatten(1)
|
55
|
+
end
|
56
|
+
|
57
|
+
def spec(name, version, platform = nil)
|
58
|
+
Bundler::CompactIndexClient.debug { "spec(name = #{name}, version = #{version}, platform = #{platform})" }
|
59
|
+
update_info(name)
|
60
|
+
@cache.specific_dependency(name, version, platform)
|
61
|
+
end
|
62
|
+
|
63
|
+
def update_and_parse_checksums!
|
64
|
+
Bundler::CompactIndexClient.debug { "update_and_parse_checksums!" }
|
65
|
+
return @info_checksums_by_name if @parsed_checksums
|
66
|
+
update(@cache.versions_path, "versions")
|
67
|
+
@info_checksums_by_name = @cache.checksums
|
68
|
+
@parsed_checksums = true
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def update(local_path, remote_path)
|
74
|
+
Bundler::CompactIndexClient.debug { "update(#{local_path}, #{remote_path})" }
|
75
|
+
unless @endpoints.add?(remote_path)
|
76
|
+
Bundler::CompactIndexClient.debug { "already fetched #{remote_path}" }
|
77
|
+
return
|
78
|
+
end
|
79
|
+
@updater.update(local_path, url(remote_path))
|
80
|
+
end
|
81
|
+
|
82
|
+
def update_info(name)
|
83
|
+
Bundler::CompactIndexClient.debug { "update_info(#{name})" }
|
84
|
+
path = @cache.info_path(name)
|
85
|
+
checksum = @updater.checksum_for_file(path)
|
86
|
+
unless existing = @info_checksums_by_name[name]
|
87
|
+
Bundler::CompactIndexClient.debug { "skipping updating info for #{name} since it is missing from versions" }
|
88
|
+
return
|
89
|
+
end
|
90
|
+
if checksum == existing
|
91
|
+
Bundler::CompactIndexClient.debug { "skipping updating info for #{name} since the versions checksum matches the local checksum" }
|
92
|
+
return
|
93
|
+
end
|
94
|
+
Bundler::CompactIndexClient.debug { "updating info for #{name} since the versions checksum #{existing} != the local checksum #{checksum}" }
|
95
|
+
update(path, "info/#{name}")
|
96
|
+
end
|
97
|
+
|
98
|
+
def url(path)
|
99
|
+
path
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "digest/md5"
|
3
|
+
|
4
|
+
module Bundler
|
5
|
+
class CompactIndexClient
|
6
|
+
class Cache
|
7
|
+
attr_reader :directory
|
8
|
+
|
9
|
+
def initialize(directory)
|
10
|
+
@directory = Pathname.new(directory).expand_path
|
11
|
+
info_roots.each do |dir|
|
12
|
+
SharedHelpers.filesystem_access(dir) do
|
13
|
+
FileUtils.mkdir_p(dir)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def names
|
19
|
+
lines(names_path)
|
20
|
+
end
|
21
|
+
|
22
|
+
def names_path
|
23
|
+
directory.join("names")
|
24
|
+
end
|
25
|
+
|
26
|
+
def versions
|
27
|
+
versions_by_name = Hash.new {|hash, key| hash[key] = [] }
|
28
|
+
info_checksums_by_name = {}
|
29
|
+
|
30
|
+
lines(versions_path).each do |line|
|
31
|
+
name, versions_string, info_checksum = line.split(" ", 3)
|
32
|
+
info_checksums_by_name[name] = info_checksum || ""
|
33
|
+
versions_string.split(",").each do |version|
|
34
|
+
if version.start_with?("-")
|
35
|
+
version = version[1..-1].split("-", 2).unshift(name)
|
36
|
+
versions_by_name[name].delete(version)
|
37
|
+
else
|
38
|
+
version = version.split("-", 2).unshift(name)
|
39
|
+
versions_by_name[name] << version
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
[versions_by_name, info_checksums_by_name]
|
45
|
+
end
|
46
|
+
|
47
|
+
def versions_path
|
48
|
+
directory.join("versions")
|
49
|
+
end
|
50
|
+
|
51
|
+
def checksums
|
52
|
+
checksums = {}
|
53
|
+
|
54
|
+
lines(versions_path).each do |line|
|
55
|
+
name, _, checksum = line.split(" ", 3)
|
56
|
+
checksums[name] = checksum
|
57
|
+
end
|
58
|
+
|
59
|
+
checksums
|
60
|
+
end
|
61
|
+
|
62
|
+
def dependencies(name)
|
63
|
+
lines(info_path(name)).map do |line|
|
64
|
+
parse_gem(line)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def info_path(name)
|
69
|
+
name = name.to_s
|
70
|
+
if name =~ /[^a-z0-9_-]/
|
71
|
+
name += "-#{Digest::MD5.hexdigest(name).downcase}"
|
72
|
+
info_roots.last.join(name)
|
73
|
+
else
|
74
|
+
info_roots.first.join(name)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def specific_dependency(name, version, platform)
|
79
|
+
pattern = [version, platform].compact.join("-")
|
80
|
+
return nil if pattern.empty?
|
81
|
+
|
82
|
+
gem_lines = info_path(name).read
|
83
|
+
gem_line = gem_lines[/^#{Regexp.escape(pattern)}\b.*/, 0]
|
84
|
+
gem_line ? parse_gem(gem_line) : nil
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def lines(path)
|
90
|
+
return [] unless path.file?
|
91
|
+
lines = SharedHelpers.filesystem_access(path, :read, &:read).split("\n")
|
92
|
+
header = lines.index("---")
|
93
|
+
header ? lines[header + 1..-1] : lines
|
94
|
+
end
|
95
|
+
|
96
|
+
def parse_gem(string)
|
97
|
+
version_and_platform, rest = string.split(" ", 2)
|
98
|
+
version, platform = version_and_platform.split("-", 2)
|
99
|
+
dependencies, requirements = rest.split("|", 2).map {|s| s.split(",") } if rest
|
100
|
+
dependencies = dependencies ? dependencies.map {|d| parse_dependency(d) } : []
|
101
|
+
requirements = requirements ? requirements.map {|r| parse_dependency(r) } : []
|
102
|
+
[version, platform, dependencies, requirements]
|
103
|
+
end
|
104
|
+
|
105
|
+
def parse_dependency(string)
|
106
|
+
dependency = string.split(":")
|
107
|
+
dependency[-1] = dependency[-1].split("&") if dependency.size > 1
|
108
|
+
dependency
|
109
|
+
end
|
110
|
+
|
111
|
+
def info_roots
|
112
|
+
[
|
113
|
+
directory.join("info"),
|
114
|
+
directory.join("info-special-characters"),
|
115
|
+
]
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "fileutils"
|
3
|
+
require "stringio"
|
4
|
+
require "tmpdir"
|
5
|
+
require "zlib"
|
6
|
+
|
7
|
+
module Bundler
|
8
|
+
class CompactIndexClient
|
9
|
+
class Updater
|
10
|
+
class MisMatchedChecksumError < Error
|
11
|
+
def initialize(path, server_checksum, local_checksum)
|
12
|
+
@path = path
|
13
|
+
@server_checksum = server_checksum
|
14
|
+
@local_checksum = local_checksum
|
15
|
+
end
|
16
|
+
|
17
|
+
def message
|
18
|
+
"The checksum of /#{@path} does not match the checksum provided by the server! Something is wrong " \
|
19
|
+
"(local checksum is #{@local_checksum.inspect}, was expecting #{@server_checksum.inspect})."
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(fetcher)
|
24
|
+
@fetcher = fetcher
|
25
|
+
end
|
26
|
+
|
27
|
+
def update(local_path, remote_path, retrying = nil)
|
28
|
+
headers = {}
|
29
|
+
|
30
|
+
Dir.mktmpdir("bundler-compact-index-") do |local_temp_dir|
|
31
|
+
local_temp_path = Pathname.new(local_temp_dir).join(local_path.basename)
|
32
|
+
|
33
|
+
# first try to fetch any new bytes on the existing file
|
34
|
+
if retrying.nil? && local_path.file?
|
35
|
+
FileUtils.cp local_path, local_temp_path
|
36
|
+
headers["If-None-Match"] = etag_for(local_temp_path)
|
37
|
+
headers["Range"] = "bytes=#{local_temp_path.size}-"
|
38
|
+
else
|
39
|
+
# Fastly ignores Range when Accept-Encoding: gzip is set
|
40
|
+
headers["Accept-Encoding"] = "gzip"
|
41
|
+
end
|
42
|
+
|
43
|
+
response = @fetcher.call(remote_path, headers)
|
44
|
+
return nil if response.is_a?(Net::HTTPNotModified)
|
45
|
+
|
46
|
+
content = response.body
|
47
|
+
if response["Content-Encoding"] == "gzip"
|
48
|
+
content = Zlib::GzipReader.new(StringIO.new(content)).read
|
49
|
+
end
|
50
|
+
|
51
|
+
mode = response.is_a?(Net::HTTPPartialContent) ? "a" : "w"
|
52
|
+
SharedHelpers.filesystem_access(local_temp_path) do
|
53
|
+
local_temp_path.open(mode) {|f| f << content }
|
54
|
+
end
|
55
|
+
|
56
|
+
response_etag = response["ETag"].gsub(%r{\AW/}, "")
|
57
|
+
if etag_for(local_temp_path) == response_etag
|
58
|
+
SharedHelpers.filesystem_access(local_path) do
|
59
|
+
FileUtils.mv(local_temp_path, local_path)
|
60
|
+
end
|
61
|
+
return nil
|
62
|
+
end
|
63
|
+
|
64
|
+
if retrying
|
65
|
+
raise MisMatchedChecksumError.new(remote_path, response_etag, etag_for(local_temp_path))
|
66
|
+
end
|
67
|
+
|
68
|
+
update(local_path, remote_path, :retrying)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def etag_for(path)
|
73
|
+
sum = checksum_for_file(path)
|
74
|
+
sum ? %("#{sum}") : nil
|
75
|
+
end
|
76
|
+
|
77
|
+
def checksum_for_file(path)
|
78
|
+
return nil unless path.file?
|
79
|
+
# This must use IO.read instead of Digest.file().hexdigest
|
80
|
+
# because we need to preserve \n line endings on windows when calculating
|
81
|
+
# the checksum
|
82
|
+
SharedHelpers.filesystem_access(path, :read) do
|
83
|
+
Digest::MD5.hexdigest(IO.read(path))
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -57,15 +57,15 @@ module Bundler
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def mswin64?
|
60
|
-
Bundler::WINDOWS &&
|
60
|
+
Bundler::WINDOWS && Bundler.local_platform.os == "mswin64" && Bundler.local_platform.cpu == "x64"
|
61
61
|
end
|
62
62
|
|
63
63
|
def mingw?
|
64
|
-
Bundler::WINDOWS &&
|
64
|
+
Bundler::WINDOWS && Bundler.local_platform.os == "mingw32" && Bundler.local_platform.cpu != "x64"
|
65
65
|
end
|
66
66
|
|
67
67
|
def x64_mingw?
|
68
|
-
Bundler::WINDOWS &&
|
68
|
+
Bundler::WINDOWS && Bundler.local_platform.os == "mingw32" && Bundler.local_platform.cpu == "x64"
|
69
69
|
end
|
70
70
|
|
71
71
|
(KNOWN_MINOR_VERSIONS + KNOWN_MAJOR_VERSIONS).each do |version|
|
@@ -7,7 +7,15 @@ module Bundler
|
|
7
7
|
class Definition
|
8
8
|
include GemHelpers
|
9
9
|
|
10
|
-
attr_reader
|
10
|
+
attr_reader(
|
11
|
+
:dependencies,
|
12
|
+
:gem_version_promoter,
|
13
|
+
:locked_deps,
|
14
|
+
:locked_gems,
|
15
|
+
:platforms,
|
16
|
+
:requires,
|
17
|
+
:ruby_version
|
18
|
+
)
|
11
19
|
|
12
20
|
# Given a gemfile and lockfile creates a Bundler definition
|
13
21
|
#
|
@@ -54,21 +62,23 @@ module Bundler
|
|
54
62
|
@specs = nil
|
55
63
|
@ruby_version = ruby_version
|
56
64
|
|
65
|
+
@lockfile = lockfile
|
57
66
|
@lockfile_contents = String.new
|
58
67
|
@locked_bundler_version = nil
|
59
68
|
@locked_ruby_version = nil
|
60
69
|
|
61
70
|
if lockfile && File.exist?(lockfile)
|
62
71
|
@lockfile_contents = Bundler.read_file(lockfile)
|
63
|
-
|
64
|
-
@
|
65
|
-
@
|
66
|
-
@
|
72
|
+
@locked_gems = LockfileParser.new(@lockfile_contents)
|
73
|
+
@locked_platforms = @locked_gems.platforms
|
74
|
+
@platforms = @locked_platforms.dup
|
75
|
+
@locked_bundler_version = @locked_gems.bundler_version
|
76
|
+
@locked_ruby_version = @locked_gems.ruby_version
|
67
77
|
|
68
78
|
if unlock != true
|
69
|
-
@locked_deps =
|
70
|
-
@locked_specs = SpecSet.new(
|
71
|
-
@locked_sources =
|
79
|
+
@locked_deps = @locked_gems.dependencies
|
80
|
+
@locked_specs = SpecSet.new(@locked_gems.specs)
|
81
|
+
@locked_sources = @locked_gems.sources
|
72
82
|
else
|
73
83
|
@unlock = {}
|
74
84
|
@locked_deps = []
|
@@ -78,33 +88,37 @@ module Bundler
|
|
78
88
|
else
|
79
89
|
@unlock = {}
|
80
90
|
@platforms = []
|
91
|
+
@locked_gems = nil
|
81
92
|
@locked_deps = []
|
82
93
|
@locked_specs = SpecSet.new([])
|
83
94
|
@locked_sources = []
|
95
|
+
@locked_platforms = []
|
84
96
|
end
|
85
97
|
|
86
98
|
@unlock[:gems] ||= []
|
87
99
|
@unlock[:sources] ||= []
|
88
|
-
@unlock[:ruby] ||= if @ruby_version &&
|
89
|
-
unless locked_ruby_version_object = RubyVersion.from_string(@locked_ruby_version)
|
90
|
-
raise LockfileError, "Failed to create a `RubyVersion` object from " \
|
91
|
-
"`#{@locked_ruby_version}` found in #{lockfile} -- try running `bundle update --ruby`."
|
92
|
-
end
|
100
|
+
@unlock[:ruby] ||= if @ruby_version && locked_ruby_version_object
|
93
101
|
@ruby_version.diff(locked_ruby_version_object)
|
94
102
|
end
|
95
|
-
@unlocking ||= @unlock[:ruby]
|
103
|
+
@unlocking ||= @unlock[:ruby] ||= (!@locked_ruby_version ^ !@ruby_version)
|
96
104
|
|
97
|
-
|
98
|
-
add_platform(current_platform)
|
105
|
+
add_current_platform unless Bundler.settings[:frozen]
|
99
106
|
|
100
107
|
@path_changes = converge_paths
|
101
|
-
|
102
|
-
@unlock[:
|
108
|
+
|
109
|
+
unless @unlock[:lock_shared_dependencies]
|
110
|
+
eager_unlock = expand_dependencies(@unlock[:gems])
|
111
|
+
@unlock[:gems] = @locked_specs.for(eager_unlock).map(&:name)
|
112
|
+
end
|
113
|
+
|
114
|
+
@gem_version_promoter = create_gem_version_promoter
|
103
115
|
|
104
116
|
@source_changes = converge_sources
|
105
117
|
@dependency_changes = converge_dependencies
|
106
118
|
@local_changes = converge_locals
|
107
119
|
|
120
|
+
@requires = compute_requires
|
121
|
+
|
108
122
|
fixup_dependency_types!
|
109
123
|
end
|
110
124
|
|
@@ -123,6 +137,19 @@ module Bundler
|
|
123
137
|
end
|
124
138
|
end
|
125
139
|
|
140
|
+
def create_gem_version_promoter
|
141
|
+
locked_specs =
|
142
|
+
if @unlocking && @locked_specs.empty? && !@lockfile_contents.empty?
|
143
|
+
# Definition uses an empty set of locked_specs to indicate all gems
|
144
|
+
# are unlocked, but GemVersionPromoter needs the locked_specs
|
145
|
+
# for conservative comparison.
|
146
|
+
Bundler::SpecSet.new(@locked_gems.specs)
|
147
|
+
else
|
148
|
+
@locked_specs
|
149
|
+
end
|
150
|
+
GemVersionPromoter.new(locked_specs, @unlock[:gems])
|
151
|
+
end
|
152
|
+
|
126
153
|
def resolve_with_cache!
|
127
154
|
raise "Specs already loaded" if @specs
|
128
155
|
sources.cached!
|
@@ -149,7 +176,7 @@ module Bundler
|
|
149
176
|
rescue GemNotFound => e # Handle yanked gem
|
150
177
|
gem_name, gem_version = extract_gem_info(e)
|
151
178
|
locked_gem = @locked_specs[gem_name].last
|
152
|
-
raise if locked_gem.nil? || locked_gem.version.to_s != gem_version
|
179
|
+
raise if locked_gem.nil? || locked_gem.version.to_s != gem_version || !@remote
|
153
180
|
raise GemNotFound, "Your bundle is locked to #{locked_gem}, but that version could not " \
|
154
181
|
"be found in any of the sources listed in your Gemfile. If you haven't changed sources, " \
|
155
182
|
"that means the author of #{locked_gem} has removed it. You'll need to update your bundle " \
|
@@ -198,7 +225,7 @@ module Bundler
|
|
198
225
|
end
|
199
226
|
|
200
227
|
def current_dependencies
|
201
|
-
dependencies.
|
228
|
+
dependencies.select(&:should_include?)
|
202
229
|
end
|
203
230
|
|
204
231
|
def specs_for(groups)
|
@@ -220,8 +247,8 @@ module Bundler
|
|
220
247
|
last_resolve
|
221
248
|
else
|
222
249
|
# Run a resolve against the locally available gems
|
223
|
-
Bundler.ui.debug("Found changes from the lockfile, re-resolving dependencies")
|
224
|
-
last_resolve.merge Resolver.resolve(expanded_dependencies, index, source_requirements, last_resolve,
|
250
|
+
Bundler.ui.debug("Found changes from the lockfile, re-resolving dependencies because #{change_reason}")
|
251
|
+
last_resolve.merge Resolver.resolve(expanded_dependencies, index, source_requirements, last_resolve, gem_version_promoter, additional_base_requirements_for_resolve)
|
225
252
|
end
|
226
253
|
end
|
227
254
|
end
|
@@ -236,6 +263,8 @@ module Bundler
|
|
236
263
|
dependency_names -= pinned_spec_names(source.specs)
|
237
264
|
dependency_names.concat(source.unmet_deps).uniq!
|
238
265
|
end
|
266
|
+
idx << Gem::Specification.new("ruby\0", RubyVersion.system.to_gem_version_with_patchlevel)
|
267
|
+
idx << Gem::Specification.new("rubygems\0", Gem::VERSION)
|
239
268
|
end
|
240
269
|
end
|
241
270
|
|
@@ -312,6 +341,18 @@ module Bundler
|
|
312
341
|
end
|
313
342
|
end
|
314
343
|
|
344
|
+
def locked_ruby_version_object
|
345
|
+
return unless @locked_ruby_version
|
346
|
+
@locked_ruby_version_object ||= begin
|
347
|
+
unless version = RubyVersion.from_string(@locked_ruby_version)
|
348
|
+
raise LockfileError, "The Ruby version #{@locked_ruby_version} from " \
|
349
|
+
"#{@lockfile} could not be parsed. " \
|
350
|
+
"Try running bundle update --ruby to resolve this."
|
351
|
+
end
|
352
|
+
version
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
315
356
|
def to_lock
|
316
357
|
out = String.new
|
317
358
|
|
@@ -375,6 +416,11 @@ module Bundler
|
|
375
416
|
deleted = []
|
376
417
|
changed = []
|
377
418
|
|
419
|
+
new_platforms = @platforms - @locked_platforms
|
420
|
+
deleted_platforms = @locked_platforms - @platforms
|
421
|
+
added.concat new_platforms.map {|p| "* platform: #{p}" }
|
422
|
+
deleted.concat deleted_platforms.map {|p| "* platform: #{p}" }
|
423
|
+
|
378
424
|
gemfile_sources = sources.lock_sources
|
379
425
|
|
380
426
|
new_sources = gemfile_sources - @locked_sources
|
@@ -423,6 +469,11 @@ module Bundler
|
|
423
469
|
raise ProductionError, msg if added.any? || deleted.any? || changed.any?
|
424
470
|
end
|
425
471
|
|
472
|
+
def validate_runtime!
|
473
|
+
validate_ruby!
|
474
|
+
validate_platforms!
|
475
|
+
end
|
476
|
+
|
426
477
|
def validate_ruby!
|
427
478
|
return unless ruby_version
|
428
479
|
|
@@ -448,11 +499,38 @@ module Bundler
|
|
448
499
|
end
|
449
500
|
end
|
450
501
|
|
502
|
+
# TODO: refactor this so that `match_platform` can be called with two platforms
|
503
|
+
DummyPlatform = Struct.new(:platform)
|
504
|
+
class DummyPlatform; include MatchPlatform; end
|
505
|
+
def validate_platforms!
|
506
|
+
return if @platforms.any? do |bundle_platform|
|
507
|
+
bundle_platform = DummyPlatform.new(bundle_platform)
|
508
|
+
Bundler.rubygems.platforms.any? do |local_platform|
|
509
|
+
bundle_platform.match_platform(local_platform)
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
raise ProductionError, "Your bundle only supports platforms #{@platforms.map(&:to_s)} " \
|
514
|
+
"but your local platforms are #{Bundler.rubygems.platforms.map(&:to_s)}, and " \
|
515
|
+
"there's no compatible match between those two lists."
|
516
|
+
end
|
517
|
+
|
451
518
|
def add_platform(platform)
|
452
519
|
@new_platform ||= !@platforms.include?(platform)
|
453
520
|
@platforms |= [platform]
|
454
521
|
end
|
455
522
|
|
523
|
+
def remove_platform(platform)
|
524
|
+
return if @platforms.delete(Gem::Platform.new(platform))
|
525
|
+
raise InvalidOption, "Unable to remove the platform `#{platform}` since the only platforms are #{@platforms.join ", "}"
|
526
|
+
end
|
527
|
+
|
528
|
+
def add_current_platform
|
529
|
+
current_platform = Bundler.local_platform
|
530
|
+
add_platform(current_platform) if Bundler.settings[:specific_platform]
|
531
|
+
add_platform(generic(current_platform))
|
532
|
+
end
|
533
|
+
|
456
534
|
attr_reader :sources
|
457
535
|
private :sources
|
458
536
|
|
@@ -462,6 +540,27 @@ module Bundler
|
|
462
540
|
!@source_changes && !@dependency_changes && !@new_platform && !@path_changes && !@local_changes
|
463
541
|
end
|
464
542
|
|
543
|
+
def change_reason
|
544
|
+
if @unlocking
|
545
|
+
unlock_reason = @unlock.reject {|_k, v| Array(v).empty? }.map do |k, v|
|
546
|
+
if v == true
|
547
|
+
k.to_s
|
548
|
+
else
|
549
|
+
v = Array(v)
|
550
|
+
"#{k}: (#{v.join(", ")})"
|
551
|
+
end
|
552
|
+
end.join(", ")
|
553
|
+
return "bundler is unlocking #{unlock_reason}"
|
554
|
+
end
|
555
|
+
[
|
556
|
+
[@source_changes, "the list of sources changed"],
|
557
|
+
[@dependency_changes, "the dependencies in your gemfile changed"],
|
558
|
+
[@new_platform, "you added a new platform to your gemfile"],
|
559
|
+
[@path_changes, "the gemspecs for path gems changed"],
|
560
|
+
[@local_changes, "the gemspecs for git local gems changed"],
|
561
|
+
].select(&:first).map(&:last).join(", ")
|
562
|
+
end
|
563
|
+
|
465
564
|
def pretty_dep(dep, source = false)
|
466
565
|
msg = String.new(dep.name)
|
467
566
|
msg << " (#{dep.requirement})" unless dep.requirement == Gem::Requirement.default
|
@@ -470,24 +569,16 @@ module Bundler
|
|
470
569
|
end
|
471
570
|
|
472
571
|
# Check if the specs of the given source changed
|
473
|
-
# according to the locked source.
|
474
|
-
|
475
|
-
|
476
|
-
def specs_changed?(source, &block)
|
477
|
-
locked = @locked_sources.find(&block)
|
478
|
-
|
479
|
-
if locked
|
480
|
-
unlocking = @locked_specs.any? do |locked_spec|
|
481
|
-
locked_spec.source.class == locked.class && locked_spec.source != locked
|
482
|
-
end
|
483
|
-
end
|
572
|
+
# according to the locked source.
|
573
|
+
def specs_changed?(source)
|
574
|
+
locked = @locked_sources.find {|s| s == source }
|
484
575
|
|
485
|
-
!locked ||
|
576
|
+
!locked || dependencies_for_source_changed?(source, locked) || specs_for_source_changed?(source)
|
486
577
|
end
|
487
578
|
|
488
|
-
def dependencies_for_source_changed?(source)
|
579
|
+
def dependencies_for_source_changed?(source, locked_source = source)
|
489
580
|
deps_for_source = @dependencies.select {|s| s.source == source }
|
490
|
-
locked_deps_for_source = @locked_deps.select {|s| s.source ==
|
581
|
+
locked_deps_for_source = @locked_deps.select {|s| s.source == locked_source }
|
491
582
|
|
492
583
|
Set.new(deps_for_source) != Set.new(locked_deps_for_source)
|
493
584
|
end
|
@@ -514,22 +605,34 @@ module Bundler
|
|
514
605
|
end
|
515
606
|
end
|
516
607
|
|
517
|
-
locals.
|
518
|
-
changed || specs_changed?(source)
|
519
|
-
end
|
608
|
+
sources_with_changes = locals.select do |source, changed|
|
609
|
+
changed || specs_changed?(source)
|
610
|
+
end.map(&:first)
|
611
|
+
!sources_with_changes.each {|source| @unlock[:sources] << source.name }.empty?
|
520
612
|
end
|
521
613
|
|
522
614
|
def converge_paths
|
523
615
|
sources.path_sources.any? do |source|
|
524
|
-
specs_changed?(source)
|
525
|
-
ls.class == source.class && ls.path == source.path
|
526
|
-
end
|
616
|
+
specs_changed?(source)
|
527
617
|
end
|
528
618
|
end
|
529
619
|
|
620
|
+
def converge_path_source_to_gemspec_source(source)
|
621
|
+
return source unless source.instance_of?(Source::Path)
|
622
|
+
gemspec_source = sources.path_sources.find {|s| s.is_a?(Source::Gemspec) && s.as_path_source == source }
|
623
|
+
gemspec_source || source
|
624
|
+
end
|
625
|
+
|
530
626
|
def converge_sources
|
531
627
|
changes = false
|
532
628
|
|
629
|
+
@locked_sources.map! do |source|
|
630
|
+
converge_path_source_to_gemspec_source(source)
|
631
|
+
end
|
632
|
+
@locked_specs.each do |spec|
|
633
|
+
spec.source &&= converge_path_source_to_gemspec_source(spec.source)
|
634
|
+
end
|
635
|
+
|
533
636
|
# Get the Rubygems sources from the Gemfile.lock
|
534
637
|
locked_gem_sources = @locked_sources.select {|s| s.is_a?(Source::Rubygems) }
|
535
638
|
# Get the Rubygems remotes from the Gemfile
|
@@ -574,6 +677,9 @@ module Bundler
|
|
574
677
|
elsif dep.source
|
575
678
|
dep.source = sources.get(dep.source)
|
576
679
|
end
|
680
|
+
if dep.source.is_a?(Source::Gemspec)
|
681
|
+
dep.platforms.concat(@platforms.map {|p| Dependency::REVERSE_PLATFORM_MAP[p] }.flatten(1)).uniq!
|
682
|
+
end
|
577
683
|
end
|
578
684
|
Set.new(@dependencies) != Set.new(@locked_deps)
|
579
685
|
end
|
@@ -625,7 +731,7 @@ module Bundler
|
|
625
731
|
# then we unlock it.
|
626
732
|
|
627
733
|
# Path sources have special logic
|
628
|
-
if s.source.instance_of?(Source::Path)
|
734
|
+
if s.source.instance_of?(Source::Path) || s.source.instance_of?(Source::Gemspec)
|
629
735
|
other = s.source.specs[s].first
|
630
736
|
|
631
737
|
# If the spec is no longer in the path source, unlock it. This
|
@@ -667,16 +773,54 @@ module Bundler
|
|
667
773
|
@locked_specs.any? {|s| s.satisfies?(dep) && (!dep.source || s.source.include?(dep.source)) }
|
668
774
|
end
|
669
775
|
|
776
|
+
# This list of dependencies is only used in #resolve, so it's OK to add
|
777
|
+
# the metadata dependencies here
|
670
778
|
def expanded_dependencies
|
671
|
-
@expanded_dependencies ||=
|
779
|
+
@expanded_dependencies ||= begin
|
780
|
+
ruby_versions = concat_ruby_version_requirements(@ruby_version)
|
781
|
+
if ruby_versions.empty? || !@ruby_version.exact?
|
782
|
+
concat_ruby_version_requirements(RubyVersion.system)
|
783
|
+
concat_ruby_version_requirements(locked_ruby_version_object) unless @unlock[:ruby]
|
784
|
+
end
|
785
|
+
|
786
|
+
metadata_dependencies = [
|
787
|
+
Dependency.new("ruby\0", ruby_versions),
|
788
|
+
Dependency.new("rubygems\0", Gem::VERSION),
|
789
|
+
]
|
790
|
+
expand_dependencies(dependencies + metadata_dependencies, @remote)
|
791
|
+
end
|
792
|
+
end
|
793
|
+
|
794
|
+
def concat_ruby_version_requirements(ruby_version, ruby_versions = [])
|
795
|
+
return ruby_versions unless ruby_version
|
796
|
+
if ruby_version.patchlevel
|
797
|
+
ruby_versions << ruby_version.to_gem_version_with_patchlevel
|
798
|
+
else
|
799
|
+
ruby_versions.concat(ruby_version.versions.map do |version|
|
800
|
+
requirement = Gem::Requirement.new(version)
|
801
|
+
if requirement.exact?
|
802
|
+
"~> #{version}.0"
|
803
|
+
else
|
804
|
+
requirement
|
805
|
+
end
|
806
|
+
end)
|
807
|
+
end
|
672
808
|
end
|
673
809
|
|
674
810
|
def expand_dependencies(dependencies, remote = false)
|
675
811
|
deps = []
|
676
812
|
dependencies.each do |dep|
|
677
813
|
dep = Dependency.new(dep, ">= 0") unless dep.respond_to?(:name)
|
678
|
-
next
|
679
|
-
dep.gem_platforms(@platforms)
|
814
|
+
next if !remote && !dep.current_platform?
|
815
|
+
platforms = dep.gem_platforms(@platforms)
|
816
|
+
if platforms.empty?
|
817
|
+
Bundler.ui.warn \
|
818
|
+
"The dependency #{dep} will be unused by any of the platforms Bundler is installing for. " \
|
819
|
+
"Bundler is installing for #{@platforms.join ", "} but the dependency " \
|
820
|
+
"is only for #{dep.platforms.map {|p| Dependency::PLATFORM_MAP[p] }.join ", "}. " \
|
821
|
+
"To add those platforms to the bundle, run `bundle lock --add-platform #{dep.platforms.join ", "}`."
|
822
|
+
end
|
823
|
+
platforms.each do |p|
|
680
824
|
deps << DepProxy.new(dep, p) if remote || p == generic_local_platform
|
681
825
|
end
|
682
826
|
end
|
@@ -740,5 +884,25 @@ module Bundler
|
|
740
884
|
# to an array. The first element will be the gem name (e.g. foo), the second will be the version number.
|
741
885
|
error.message.scan(/Could not find (\w+)-(\d+(?:\.\d+)+)/).flatten
|
742
886
|
end
|
887
|
+
|
888
|
+
def compute_requires
|
889
|
+
dependencies.reduce({}) do |requires, dep|
|
890
|
+
next requires unless dep.should_include?
|
891
|
+
requires[dep.name] = Array(dep.autorequire || dep.name).map do |file|
|
892
|
+
# Allow `require: true` as an alias for `require: <name>`
|
893
|
+
file == true ? dep.name : file
|
894
|
+
end
|
895
|
+
requires
|
896
|
+
end
|
897
|
+
end
|
898
|
+
|
899
|
+
def additional_base_requirements_for_resolve
|
900
|
+
return [] unless @locked_gems && Bundler.feature_flag.only_update_to_newer_versions?
|
901
|
+
@locked_gems.specs.reduce({}) do |requirements, locked_spec|
|
902
|
+
dep = Gem::Dependency.new(locked_spec.name, ">= #{locked_spec.version}")
|
903
|
+
requirements[locked_spec.name] = DepProxy.new(dep, locked_spec.platform)
|
904
|
+
requirements
|
905
|
+
end.values
|
906
|
+
end
|
743
907
|
end
|
744
908
|
end
|