bundler 4.0.10 → 4.0.20
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +174 -0
- data/bundler.gemspec +2 -2
- data/lib/bundler/build_metadata.rb +2 -2
- data/lib/bundler/cli/add.rb +3 -0
- data/lib/bundler/cli/check.rb +1 -3
- data/lib/bundler/cli/common.rb +17 -0
- data/lib/bundler/cli/config.rb +8 -3
- data/lib/bundler/cli/doctor/diagnose.rb +1 -1
- data/lib/bundler/cli/exec.rb +15 -1
- data/lib/bundler/cli/gem.rb +2 -3
- data/lib/bundler/cli/init.rb +4 -0
- data/lib/bundler/cli/install.rb +4 -0
- data/lib/bundler/cli/issue.rb +2 -2
- data/lib/bundler/cli/lock.rb +10 -2
- data/lib/bundler/cli/open.rb +12 -2
- data/lib/bundler/cli/outdated.rb +76 -6
- data/lib/bundler/cli/update.rb +3 -0
- data/lib/bundler/cli.rb +21 -15
- data/lib/bundler/compact_index_client/cache.rb +11 -0
- data/lib/bundler/compact_index_client/cache_file.rb +1 -1
- data/lib/bundler/compact_index_client/parser.rb +4 -1
- data/lib/bundler/definition.rb +64 -15
- data/lib/bundler/dsl.rb +11 -4
- data/lib/bundler/endpoint_specification.rb +30 -2
- data/lib/bundler/fetcher/base.rb +3 -0
- data/lib/bundler/fetcher/compact_index.rb +13 -0
- data/lib/bundler/fetcher/gem_remote_fetcher.rb +2 -0
- data/lib/bundler/fetcher.rb +12 -2
- data/lib/bundler/gem_helper.rb +2 -2
- data/lib/bundler/installer/parallel_installer.rb +68 -23
- data/lib/bundler/installer.rb +18 -1
- data/lib/bundler/lockfile_generator.rb +55 -1
- data/lib/bundler/lockfile_parser.rb +23 -1
- data/lib/bundler/man/bundle-add.1 +6 -3
- data/lib/bundler/man/bundle-add.1.ronn +7 -2
- data/lib/bundler/man/bundle-binstubs.1 +1 -1
- data/lib/bundler/man/bundle-cache.1 +4 -1
- data/lib/bundler/man/bundle-cache.1.ronn +6 -0
- data/lib/bundler/man/bundle-check.1 +1 -1
- data/lib/bundler/man/bundle-clean.1 +1 -1
- data/lib/bundler/man/bundle-config.1 +121 -157
- data/lib/bundler/man/bundle-config.1.ronn +38 -6
- data/lib/bundler/man/bundle-console.1 +3 -3
- data/lib/bundler/man/bundle-console.1.ronn +2 -2
- data/lib/bundler/man/bundle-doctor.1 +1 -1
- data/lib/bundler/man/bundle-env.1 +1 -1
- data/lib/bundler/man/bundle-exec.1 +1 -1
- data/lib/bundler/man/bundle-fund.1 +1 -1
- data/lib/bundler/man/bundle-gem.1 +1 -1
- data/lib/bundler/man/bundle-help.1 +1 -1
- data/lib/bundler/man/bundle-info.1 +1 -1
- data/lib/bundler/man/bundle-init.1 +2 -2
- data/lib/bundler/man/bundle-init.1.ronn +1 -1
- data/lib/bundler/man/bundle-install.1 +5 -2
- data/lib/bundler/man/bundle-install.1.ronn +9 -1
- data/lib/bundler/man/bundle-issue.1 +1 -1
- data/lib/bundler/man/bundle-licenses.1 +1 -1
- data/lib/bundler/man/bundle-list.1 +1 -1
- data/lib/bundler/man/bundle-lock.1 +5 -2
- data/lib/bundler/man/bundle-lock.1.ronn +6 -0
- data/lib/bundler/man/bundle-open.1 +1 -1
- data/lib/bundler/man/bundle-outdated.1 +17 -14
- data/lib/bundler/man/bundle-outdated.1.ronn +25 -12
- data/lib/bundler/man/bundle-platform.1 +5 -1
- data/lib/bundler/man/bundle-platform.1.ronn +4 -0
- data/lib/bundler/man/bundle-plugin.1 +2 -2
- data/lib/bundler/man/bundle-plugin.1.ronn +1 -1
- data/lib/bundler/man/bundle-pristine.1 +1 -1
- data/lib/bundler/man/bundle-remove.1 +1 -1
- data/lib/bundler/man/bundle-show.1 +1 -1
- data/lib/bundler/man/bundle-update.1 +5 -2
- data/lib/bundler/man/bundle-update.1.ronn +8 -0
- data/lib/bundler/man/bundle-version.1 +1 -1
- data/lib/bundler/man/bundle.1 +23 -2
- data/lib/bundler/man/bundle.1.ronn +22 -1
- data/lib/bundler/man/gemfile.5 +14 -3
- data/lib/bundler/man/gemfile.5.ronn +23 -2
- data/lib/bundler/mirror.rb +7 -7
- data/lib/bundler/plugin/unloaded_source.rb +25 -0
- data/lib/bundler/plugin.rb +12 -6
- data/lib/bundler/remote_specification.rb +1 -1
- data/lib/bundler/resolver/base.rb +9 -0
- data/lib/bundler/resolver.rb +116 -2
- data/lib/bundler/rubygems_ext.rb +22 -0
- data/lib/bundler/rubygems_gem_installer.rb +35 -2
- data/lib/bundler/runtime.rb +11 -11
- data/lib/bundler/self_manager.rb +6 -1
- data/lib/bundler/settings.rb +1 -0
- data/lib/bundler/shared_helpers.rb +21 -1
- data/lib/bundler/source/git/git_proxy.rb +83 -10
- data/lib/bundler/source/git.rb +4 -4
- data/lib/bundler/source/metadata.rb +4 -0
- data/lib/bundler/source/path.rb +4 -3
- data/lib/bundler/source/rubygems/remote.rb +12 -2
- data/lib/bundler/source/rubygems.rb +81 -13
- data/lib/bundler/source_list.rb +44 -3
- data/lib/bundler/spec_set.rb +2 -2
- data/lib/bundler/stub_specification.rb +3 -1
- data/lib/bundler/templates/newgem/newgem.gemspec.tt +8 -2
- data/lib/bundler/vendored_tsort.rb +19 -2
- data/lib/bundler/version.rb +1 -1
- data/lib/bundler.rb +26 -6
- metadata +5 -6
- data/lib/bundler/vendor/tsort/LICENSE.txt +0 -22
- data/lib/bundler/vendor/tsort/lib/tsort.rb +0 -455
data/lib/bundler/cli.rb
CHANGED
|
@@ -61,18 +61,18 @@ module Bundler
|
|
|
61
61
|
|
|
62
62
|
current_cmd = args.last[:current_command].name
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
64
|
+
# `bundle config` manages stored settings, so avoid promoting settings
|
|
65
|
+
# like `gemfile` or `lockfile` to environment variables before it runs.
|
|
66
|
+
unless current_cmd == "config"
|
|
67
|
+
Bundler.configure_custom_gemfile(options[:gemfile])
|
|
68
|
+
|
|
69
|
+
# lock --lockfile works differently than install --lockfile
|
|
70
|
+
unless current_cmd == "lock"
|
|
71
|
+
custom_lockfile = options[:lockfile] || ENV["BUNDLE_LOCKFILE"] || Bundler.settings[:lockfile]
|
|
72
|
+
if custom_lockfile && !custom_lockfile.empty?
|
|
73
|
+
Bundler::SharedHelpers.set_env "BUNDLE_LOCKFILE", File.expand_path(custom_lockfile)
|
|
74
|
+
reset_settings = true
|
|
75
|
+
end
|
|
76
76
|
end
|
|
77
77
|
end
|
|
78
78
|
|
|
@@ -274,6 +274,7 @@ module Bundler
|
|
|
274
274
|
method_option "target-rbconfig", type: :string, banner: "Path to rbconfig.rb for the deployment target platform"
|
|
275
275
|
method_option "without", type: :array, banner: "Exclude gems that are part of the specified named group (removed)."
|
|
276
276
|
method_option "with", type: :array, banner: "Include gems that are part of the specified named group (removed)."
|
|
277
|
+
method_option "cooldown", type: :numeric, banner: "Only consider gem versions published at least N days ago. Use 0 to disable."
|
|
277
278
|
def install
|
|
278
279
|
%w[clean deployment frozen no-prune path shebang without with].each do |option|
|
|
279
280
|
remembered_flag_deprecation(option)
|
|
@@ -324,6 +325,7 @@ module Bundler
|
|
|
324
325
|
method_option "strict", type: :boolean, banner: "Do not allow any gem to be updated past latest --patch | --minor | --major"
|
|
325
326
|
method_option "conservative", type: :boolean, banner: "Use bundle install conservative update behavior and do not allow shared dependencies to be updated."
|
|
326
327
|
method_option "all", type: :boolean, banner: "Update everything."
|
|
328
|
+
method_option "cooldown", type: :numeric, banner: "Only consider gem versions published at least N days ago. Use 0 to disable."
|
|
327
329
|
def update(*gems)
|
|
328
330
|
require_relative "cli/update"
|
|
329
331
|
Bundler.settings.temporary(no_install: false) do
|
|
@@ -405,6 +407,7 @@ module Bundler
|
|
|
405
407
|
method_option "skip-install", type: :boolean, banner: "Adds gem to the Gemfile but does not install it"
|
|
406
408
|
method_option "optimistic", type: :boolean, banner: "Adds optimistic declaration of version to gem"
|
|
407
409
|
method_option "strict", type: :boolean, banner: "Adds strict declaration of version to gem"
|
|
410
|
+
method_option "cooldown", type: :numeric, banner: "Only consider gem versions published at least N days ago. Use 0 to disable."
|
|
408
411
|
def add(*gems)
|
|
409
412
|
require_relative "cli/add"
|
|
410
413
|
Add.new(options.dup, gems).run
|
|
@@ -435,6 +438,7 @@ module Bundler
|
|
|
435
438
|
method_option "filter-patch", type: :boolean, banner: "Only list patch newer versions"
|
|
436
439
|
method_option "parseable", aliases: "--porcelain", type: :boolean, banner: "Use minimal formatting for more parseable output"
|
|
437
440
|
method_option "only-explicit", type: :boolean, banner: "Only list gems specified in your Gemfile, not their dependencies"
|
|
441
|
+
method_option "cooldown", type: :numeric, banner: "Only consider gem versions published at least N days ago. Use 0 to disable."
|
|
438
442
|
def outdated(*gems)
|
|
439
443
|
require_relative "cli/outdated"
|
|
440
444
|
Outdated.new(options, gems).run
|
|
@@ -457,6 +461,7 @@ module Bundler
|
|
|
457
461
|
method_option "path", type: :string, banner: "Specify a different path than the system default, namely, $BUNDLE_PATH or $GEM_HOME (removed)."
|
|
458
462
|
method_option "quiet", type: :boolean, banner: "Only output warnings and errors."
|
|
459
463
|
method_option "frozen", type: :boolean, banner: "Do not allow the Gemfile.lock to be updated after this bundle cache operation's install (removed)"
|
|
464
|
+
method_option "cooldown", type: :numeric, banner: "Only consider gem versions published at least N days ago. Use 0 to disable."
|
|
460
465
|
long_desc <<-D
|
|
461
466
|
The cache command will copy the .gem files for every gem in the bundle into the
|
|
462
467
|
directory ./vendor/cache. If you then check that directory into your source
|
|
@@ -621,15 +626,15 @@ module Bundler
|
|
|
621
626
|
end
|
|
622
627
|
|
|
623
628
|
desc "lock", "Creates a lockfile without installing"
|
|
624
|
-
method_option "update", type: :array, lazy_default: true, banner: "ignore the existing lockfile, update all gems by default, or update list of given gems"
|
|
629
|
+
method_option "update", type: :array, lazy_default: true, repeatable: true, banner: "ignore the existing lockfile, update all gems by default, or update list of given gems"
|
|
625
630
|
method_option "local", type: :boolean, default: false, banner: "do not attempt to fetch remote gemspecs and use the local gem cache only"
|
|
626
631
|
method_option "print", type: :boolean, default: false, banner: "print the lockfile to STDOUT instead of writing to the file system"
|
|
627
632
|
method_option "gemfile", type: :string, banner: "Use the specified gemfile instead of Gemfile"
|
|
628
633
|
method_option "lockfile", type: :string, default: nil, banner: "the path the lockfile should be written to"
|
|
629
634
|
method_option "full-index", type: :boolean, default: false, banner: "Fall back to using the single-file index of all gems"
|
|
630
635
|
method_option "add-checksums", type: :boolean, default: false, banner: "Adds checksums to the lockfile"
|
|
631
|
-
method_option "add-platform", type: :array, default: [], banner: "Add a new platform to the lockfile"
|
|
632
|
-
method_option "remove-platform", type: :array, default: [], banner: "Remove a platform from the lockfile"
|
|
636
|
+
method_option "add-platform", type: :array, default: [], repeatable: true, banner: "Add a new platform to the lockfile"
|
|
637
|
+
method_option "remove-platform", type: :array, default: [], repeatable: true, banner: "Remove a platform from the lockfile"
|
|
633
638
|
method_option "normalize-platforms", type: :boolean, default: false, banner: "Normalize lockfile platforms"
|
|
634
639
|
method_option "patch", type: :boolean, banner: "If updating, prefer updating only to next patch version"
|
|
635
640
|
method_option "minor", type: :boolean, banner: "If updating, prefer updating only to next minor version"
|
|
@@ -638,6 +643,7 @@ module Bundler
|
|
|
638
643
|
method_option "strict", type: :boolean, banner: "If updating, do not allow any gem to be updated past latest --patch | --minor | --major"
|
|
639
644
|
method_option "conservative", type: :boolean, banner: "If updating, use bundle install conservative update behavior and do not allow shared dependencies to be updated"
|
|
640
645
|
method_option "bundler", type: :string, lazy_default: "> 0.a", banner: "Update the locked version of bundler"
|
|
646
|
+
method_option "cooldown", type: :numeric, banner: "Only consider gem versions published at least N days ago. Use 0 to disable."
|
|
641
647
|
def lock
|
|
642
648
|
require_relative "cli/lock"
|
|
643
649
|
Lock.new(options).run
|
|
@@ -50,6 +50,7 @@ module Bundler
|
|
|
50
50
|
|
|
51
51
|
def info_path(name)
|
|
52
52
|
name = name.to_s
|
|
53
|
+
validate_name!(name)
|
|
53
54
|
# TODO: converge this into the info_root by hashing all filenames like info_etag_path
|
|
54
55
|
if /[^a-z0-9_-]/.match?(name)
|
|
55
56
|
name += "-#{SharedHelpers.digest(:MD5).hexdigest(name).downcase}"
|
|
@@ -61,9 +62,19 @@ module Bundler
|
|
|
61
62
|
|
|
62
63
|
def info_etag_path(name)
|
|
63
64
|
name = name.to_s
|
|
65
|
+
validate_name!(name)
|
|
64
66
|
@info_etag_root.join("#{name}-#{SharedHelpers.digest(:MD5).hexdigest(name).downcase}")
|
|
65
67
|
end
|
|
66
68
|
|
|
69
|
+
# Gem names come from the remote index and are not otherwise
|
|
70
|
+
# validated, so refuse anything that would escape the cache
|
|
71
|
+
# directory when used as a path component.
|
|
72
|
+
def validate_name!(name)
|
|
73
|
+
return if File.basename(name) == name
|
|
74
|
+
|
|
75
|
+
raise SecurityError, "malformed gem name: #{name.inspect}"
|
|
76
|
+
end
|
|
77
|
+
|
|
67
78
|
def mkdir(name)
|
|
68
79
|
directory.join(name).tap do |dir|
|
|
69
80
|
SharedHelpers.filesystem_access(dir) do
|
|
@@ -103,7 +103,7 @@ module Bundler
|
|
|
103
103
|
# Returns false without appending when no digests since appending is too error prone to do without digests.
|
|
104
104
|
def append(data)
|
|
105
105
|
return false unless digests?
|
|
106
|
-
open("
|
|
106
|
+
open("ab") {|f| f.write data }
|
|
107
107
|
verify && commit
|
|
108
108
|
end
|
|
109
109
|
|
|
@@ -71,7 +71,10 @@ module Bundler
|
|
|
71
71
|
# This method gets called at least once for every gem when parsing versions.
|
|
72
72
|
def parse_version_checksum(line, checksums)
|
|
73
73
|
return unless (name_end = line.index(" ")) # Artifactory bug causes blank lines in artifactor index files
|
|
74
|
-
|
|
74
|
+
checksum_start = line.index(" ", name_end + 1)
|
|
75
|
+
return unless checksum_start
|
|
76
|
+
checksum_start += 1
|
|
77
|
+
|
|
75
78
|
checksum_end = line.size - checksum_start
|
|
76
79
|
|
|
77
80
|
line.freeze # allows slicing into the string to not allocate a copy of the line
|
data/lib/bundler/definition.rb
CHANGED
|
@@ -230,6 +230,16 @@ module Bundler
|
|
|
230
230
|
sources.prefer_local!
|
|
231
231
|
end
|
|
232
232
|
|
|
233
|
+
# Releases memory only needed during resolution, such as remote spec
|
|
234
|
+
# indexes and resolver state. Only safe to call once resolution is
|
|
235
|
+
# complete and the result has been materialized, since any further
|
|
236
|
+
# resolution will need to refetch remote specs.
|
|
237
|
+
def release_resolution_memory!
|
|
238
|
+
@resolver = nil
|
|
239
|
+
@resolution_base = nil
|
|
240
|
+
sources.release_resolution_memory!
|
|
241
|
+
end
|
|
242
|
+
|
|
233
243
|
# For given dependency list returns a SpecSet with Gemspec of all the required
|
|
234
244
|
# dependencies.
|
|
235
245
|
# 1. The method first resolves the dependencies specified in Gemfile
|
|
@@ -356,6 +366,12 @@ module Bundler
|
|
|
356
366
|
sources.git_sources.filter_map {|s| File.realpath(s.path) if File.exist?(s.path) }
|
|
357
367
|
end
|
|
358
368
|
|
|
369
|
+
# Versions excluded by cooldown during the last resolution, one entry per
|
|
370
|
+
# gem with the newest skipped version. Empty when no resolution ran.
|
|
371
|
+
def cooldown_skipped
|
|
372
|
+
@cooldown_skipped || []
|
|
373
|
+
end
|
|
374
|
+
|
|
359
375
|
def groups
|
|
360
376
|
dependencies.flat_map(&:groups).uniq
|
|
361
377
|
end
|
|
@@ -387,10 +403,6 @@ module Bundler
|
|
|
387
403
|
|
|
388
404
|
contents = to_lock
|
|
389
405
|
|
|
390
|
-
# Convert to \r\n if the existing lock has them
|
|
391
|
-
# i.e., Windows with `git config core.autocrlf=true`
|
|
392
|
-
contents.gsub!(/\n/, "\r\n") if @lockfile_contents.match?("\r\n")
|
|
393
|
-
|
|
394
406
|
if @locked_bundler_version
|
|
395
407
|
locked_major = @locked_bundler_version.segments.first
|
|
396
408
|
current_major = bundler_version_to_lock.segments.first
|
|
@@ -398,7 +410,7 @@ module Bundler
|
|
|
398
410
|
updating_major = locked_major < current_major
|
|
399
411
|
end
|
|
400
412
|
|
|
401
|
-
preserve_unknown_sections ||=
|
|
413
|
+
preserve_unknown_sections ||= Bundler.frozen_bundle? || (!updating_major && !(unlocking? || @unlocking_bundler))
|
|
402
414
|
|
|
403
415
|
if File.exist?(file) && lockfiles_equal?(@lockfile_contents, contents, preserve_unknown_sections)
|
|
404
416
|
return if Bundler.frozen_bundle?
|
|
@@ -407,8 +419,18 @@ module Bundler
|
|
|
407
419
|
end
|
|
408
420
|
|
|
409
421
|
if Bundler.frozen_bundle?
|
|
410
|
-
|
|
411
|
-
|
|
422
|
+
msg = lockfile_changes_summary("frozen mode is set") ||
|
|
423
|
+
"Your lockfile needs to be updated, but it can't be because frozen mode is set.\n\n" \
|
|
424
|
+
"Run `bundle install` elsewhere and add the updated #{SharedHelpers.relative_lockfile_path} to version control."
|
|
425
|
+
raise ProductionError, msg
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
# Convert to \r\n if the existing lock has them, i.e., Windows with
|
|
429
|
+
# `git config core.autocrlf=true`. Detect from the bytes on disk because
|
|
430
|
+
# reading in text mode strips carriage returns on Windows, which would
|
|
431
|
+
# otherwise defeat this check and rewrite a `\r\n` lockfile with `\n`.
|
|
432
|
+
if File.exist?(file) && SharedHelpers.filesystem_access(file, :read) {|p| File.binread(p).include?("\r\n") }
|
|
433
|
+
contents.gsub!(/\n/, "\r\n")
|
|
412
434
|
end
|
|
413
435
|
|
|
414
436
|
begin
|
|
@@ -688,9 +710,10 @@ module Bundler
|
|
|
688
710
|
"available locally before rerunning Bundler."
|
|
689
711
|
else
|
|
690
712
|
"Your bundle is locked to #{locked_gem} from #{locked_gem.source}, but that version can " \
|
|
691
|
-
"no longer be found in that source. That means the author of #{locked_gem} has removed it
|
|
692
|
-
"You'll need to update your bundle to a version other
|
|
693
|
-
"removed
|
|
713
|
+
"no longer be found in that source. That means either the author of #{locked_gem} has removed it, " \
|
|
714
|
+
"or you no longer have access to that source. You'll need to update your bundle to a version other " \
|
|
715
|
+
"than #{locked_gem} that hasn't been removed, or check your credentials and access rights for " \
|
|
716
|
+
"#{locked_gem.source}, in order to install."
|
|
694
717
|
end
|
|
695
718
|
|
|
696
719
|
raise GemNotFound, message
|
|
@@ -755,6 +778,8 @@ module Bundler
|
|
|
755
778
|
|
|
756
779
|
result = SpecSet.new(resolver.start)
|
|
757
780
|
|
|
781
|
+
@cooldown_skipped = resolver.cooldown_skipped
|
|
782
|
+
|
|
758
783
|
@resolved_bundler_version = result.find {|spec| spec.name == "bundler" }&.version
|
|
759
784
|
|
|
760
785
|
@new_platforms.each do |platform|
|
|
@@ -783,7 +808,25 @@ module Bundler
|
|
|
783
808
|
end
|
|
784
809
|
|
|
785
810
|
def precompute_source_requirements_for_indirect_dependencies?
|
|
786
|
-
sources.non_global_rubygems_sources.all?(&:dependency_api_available?)
|
|
811
|
+
if sources.non_global_rubygems_sources.all?(&:dependency_api_available?)
|
|
812
|
+
true
|
|
813
|
+
else
|
|
814
|
+
non_dependency_api_warning
|
|
815
|
+
false
|
|
816
|
+
end
|
|
817
|
+
end
|
|
818
|
+
|
|
819
|
+
def non_dependency_api_warning
|
|
820
|
+
non_api_sources = sources.non_global_rubygems_sources.reject(&:dependency_api_available?)
|
|
821
|
+
non_api_source_names = non_api_sources.map {|d| " * #{d}" }.join("\n")
|
|
822
|
+
|
|
823
|
+
msg = String.new
|
|
824
|
+
msg << "Your Gemfile contains scoped sources that don't implement a dependency API, namely:\n\n"
|
|
825
|
+
msg << non_api_source_names
|
|
826
|
+
msg << "\n\nUsing the above gem servers may result in installing unexpected gems. " \
|
|
827
|
+
"To resolve this warning, make sure you use gem servers that implement dependency APIs, " \
|
|
828
|
+
"such as gemstash or geminabox gem servers."
|
|
829
|
+
Bundler.ui.warn msg
|
|
787
830
|
end
|
|
788
831
|
|
|
789
832
|
def current_platform_locked?
|
|
@@ -988,6 +1031,8 @@ module Bundler
|
|
|
988
1031
|
end
|
|
989
1032
|
end
|
|
990
1033
|
|
|
1034
|
+
sources.metadata_source.checksum_store.merge!(@locked_gems.metadata_source.checksum_store) if @locked_gems
|
|
1035
|
+
|
|
991
1036
|
changes
|
|
992
1037
|
end
|
|
993
1038
|
|
|
@@ -1157,16 +1202,20 @@ module Bundler
|
|
|
1157
1202
|
def find_source_requirements
|
|
1158
1203
|
preload_git_sources
|
|
1159
1204
|
|
|
1205
|
+
# Only safe to exclude when locked_requirements (merged below) backfills the gap.
|
|
1206
|
+
nothing_changed = nothing_changed?
|
|
1207
|
+
excluded = nothing_changed ? excluded_git_sources : []
|
|
1208
|
+
|
|
1160
1209
|
# Record the specs available in each gem's source, so that those
|
|
1161
1210
|
# specs will be available later when the resolver knows where to
|
|
1162
1211
|
# look for that gemspec (or its dependencies)
|
|
1163
1212
|
source_requirements = if precompute_source_requirements_for_indirect_dependencies?
|
|
1164
|
-
all_requirements = source_map.all_requirements(
|
|
1213
|
+
all_requirements = source_map.all_requirements(excluded)
|
|
1165
1214
|
{ default: default_source }.merge(all_requirements)
|
|
1166
1215
|
else
|
|
1167
|
-
{ default: Source::RubygemsAggregate.new(sources, source_map,
|
|
1216
|
+
{ default: Source::RubygemsAggregate.new(sources, source_map, excluded) }.merge(source_map.direct_requirements)
|
|
1168
1217
|
end
|
|
1169
|
-
source_requirements.merge!(source_map.locked_requirements) if nothing_changed
|
|
1218
|
+
source_requirements.merge!(source_map.locked_requirements) if nothing_changed
|
|
1170
1219
|
metadata_dependencies.each do |dep|
|
|
1171
1220
|
source_requirements[dep.name] = sources.metadata_source
|
|
1172
1221
|
end
|
|
@@ -1259,7 +1308,7 @@ module Bundler
|
|
|
1259
1308
|
|
|
1260
1309
|
def new_resolution_base(last_resolve:, unlock:)
|
|
1261
1310
|
new_resolution_platforms = @current_platform_missing ? @new_platforms + [Bundler.local_platform] : @new_platforms
|
|
1262
|
-
Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, locked_specs: @originally_locked_specs, unlock: unlock, prerelease: gem_version_promoter.pre?, prefer_local: @prefer_local, new_platforms: new_resolution_platforms)
|
|
1311
|
+
Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, locked_specs: @originally_locked_specs, unlock: unlock, prerelease: gem_version_promoter.pre?, prefer_local: @prefer_local, new_platforms: new_resolution_platforms, explicit_unlocks: @explicit_unlocks)
|
|
1263
1312
|
end
|
|
1264
1313
|
|
|
1265
1314
|
def new_resolver(base)
|
data/lib/bundler/dsl.rb
CHANGED
|
@@ -68,7 +68,7 @@ module Bundler
|
|
|
68
68
|
development_group = opts[:development_group] || :development
|
|
69
69
|
expanded_path = gemfile_root.join(path)
|
|
70
70
|
|
|
71
|
-
gemspecs =
|
|
71
|
+
gemspecs = SharedHelpers.glob_files_in_dir("{,*}.gemspec", expanded_path).filter_map {|g| Bundler.load_gemspec(g) }
|
|
72
72
|
gemspecs.reject! {|s| s.name != name } if name
|
|
73
73
|
specs_by_name_and_version = gemspecs.group_by {|s| [s.name, s.version] }
|
|
74
74
|
|
|
@@ -116,6 +116,10 @@ module Bundler
|
|
|
116
116
|
options = args.last.is_a?(Hash) ? args.pop.dup : {}
|
|
117
117
|
options = normalize_hash(options)
|
|
118
118
|
source = normalize_source(source)
|
|
119
|
+
cooldown = options["cooldown"]
|
|
120
|
+
if cooldown && !(cooldown.is_a?(Integer) && cooldown >= 0)
|
|
121
|
+
raise InvalidOption, "Expected `cooldown` to be a non-negative integer, got #{cooldown.inspect}"
|
|
122
|
+
end
|
|
119
123
|
|
|
120
124
|
if options.key?("type")
|
|
121
125
|
options["type"] = options["type"].to_s
|
|
@@ -130,9 +134,9 @@ module Bundler
|
|
|
130
134
|
source_opts = options.merge("uri" => source)
|
|
131
135
|
with_source(@sources.add_plugin_source(options["type"], source_opts), &blk)
|
|
132
136
|
elsif block_given?
|
|
133
|
-
with_source(@sources.add_rubygems_source("remotes" => source), &blk)
|
|
137
|
+
with_source(@sources.add_rubygems_source("remotes" => source, "cooldown" => cooldown), &blk)
|
|
134
138
|
else
|
|
135
|
-
@sources.add_global_rubygems_remote(source)
|
|
139
|
+
@sources.add_global_rubygems_remote(source, cooldown: cooldown)
|
|
136
140
|
end
|
|
137
141
|
end
|
|
138
142
|
|
|
@@ -598,8 +602,11 @@ module Bundler
|
|
|
598
602
|
|
|
599
603
|
trace_line = backtrace.find {|l| l.include?(dsl_path) } || trace_line
|
|
600
604
|
return m unless trace_line
|
|
601
|
-
|
|
605
|
+
# Match the line number right before `:in` or the end of the line so a
|
|
606
|
+
# Windows drive letter like `C:` does not get mistaken for the number.
|
|
607
|
+
line_number = trace_line[/:(\d+)(?::in\b|\z)/, 1]
|
|
602
608
|
return m unless line_number
|
|
609
|
+
line_number = line_number.to_i - 1
|
|
603
610
|
|
|
604
611
|
lines = contents.lines.to_a
|
|
605
612
|
indent = " # "
|
|
@@ -5,7 +5,7 @@ module Bundler
|
|
|
5
5
|
class EndpointSpecification < Gem::Specification
|
|
6
6
|
include MatchRemoteMetadata
|
|
7
7
|
|
|
8
|
-
attr_reader :name, :version, :platform, :checksum
|
|
8
|
+
attr_reader :name, :version, :platform, :checksum, :created_at
|
|
9
9
|
attr_writer :dependencies
|
|
10
10
|
attr_accessor :remote, :locked_platform
|
|
11
11
|
|
|
@@ -50,6 +50,19 @@ module Bundler
|
|
|
50
50
|
end
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
+
# `require_paths` is overridden above, but `full_require_paths` (and so
|
|
54
|
+
# `load_paths`) is computed from `raw_require_paths`, which would otherwise
|
|
55
|
+
# report the default `lib` for every gem
|
|
56
|
+
def raw_require_paths
|
|
57
|
+
if @remote_specification
|
|
58
|
+
@remote_specification.raw_require_paths
|
|
59
|
+
elsif _local_specification
|
|
60
|
+
_local_specification.raw_require_paths
|
|
61
|
+
else
|
|
62
|
+
super
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
53
66
|
# needed for inline
|
|
54
67
|
def load_paths
|
|
55
68
|
# remote specs aren't installed, and can't have load_paths
|
|
@@ -145,6 +158,7 @@ module Bundler
|
|
|
145
158
|
unless data
|
|
146
159
|
@required_ruby_version = nil
|
|
147
160
|
@required_rubygems_version = nil
|
|
161
|
+
@created_at = nil
|
|
148
162
|
return
|
|
149
163
|
end
|
|
150
164
|
|
|
@@ -152,8 +166,13 @@ module Bundler
|
|
|
152
166
|
next unless v
|
|
153
167
|
case k.to_s
|
|
154
168
|
when "checksum"
|
|
169
|
+
# Some registries send empty checksum values, treat them as if the
|
|
170
|
+
# checksum was not included at all
|
|
171
|
+
checksum = v.last
|
|
172
|
+
next unless checksum
|
|
173
|
+
|
|
155
174
|
begin
|
|
156
|
-
@checksum = Checksum.from_api(
|
|
175
|
+
@checksum = Checksum.from_api(checksum, @spec_fetcher.uri)
|
|
157
176
|
rescue ArgumentError => e
|
|
158
177
|
raise ArgumentError, "Invalid checksum for #{full_name}: #{e.message}"
|
|
159
178
|
end
|
|
@@ -161,6 +180,15 @@ module Bundler
|
|
|
161
180
|
@required_rubygems_version = Gem::Requirement.new(v)
|
|
162
181
|
when "ruby"
|
|
163
182
|
@required_ruby_version = Gem::Requirement.new(v)
|
|
183
|
+
when "created_at"
|
|
184
|
+
value = v.is_a?(Array) ? v.last : v
|
|
185
|
+
if value.is_a?(String)
|
|
186
|
+
@created_at = begin
|
|
187
|
+
Time.new(value)
|
|
188
|
+
rescue ArgumentError
|
|
189
|
+
nil
|
|
190
|
+
end
|
|
191
|
+
end
|
|
164
192
|
end
|
|
165
193
|
end
|
|
166
194
|
rescue StandardError => e
|
data/lib/bundler/fetcher/base.rb
CHANGED
|
@@ -63,6 +63,13 @@ module Bundler
|
|
|
63
63
|
true
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
+
# The client holds the parsed checksums of all info files in the
|
|
67
|
+
# index. Dropping it is always safe because it is rebuilt from the
|
|
68
|
+
# local cache on demand.
|
|
69
|
+
def release_resolution_memory!
|
|
70
|
+
@compact_index_client = nil
|
|
71
|
+
end
|
|
72
|
+
|
|
66
73
|
private
|
|
67
74
|
|
|
68
75
|
def compact_index_client
|
|
@@ -73,6 +80,12 @@ module Bundler
|
|
|
73
80
|
end
|
|
74
81
|
|
|
75
82
|
def fetch_gem_infos(names)
|
|
83
|
+
# Create the client and update the versions file on this thread.
|
|
84
|
+
# Otherwise the workers race to lazily create the client and update
|
|
85
|
+
# the versions file concurrently, e.g. when the client was released
|
|
86
|
+
# after resolution and is being rebuilt for `bundle cache`.
|
|
87
|
+
compact_index_client.available?
|
|
88
|
+
|
|
76
89
|
in_parallel(names) {|name| compact_index_client.info(name) }
|
|
77
90
|
rescue TooManyRequestsError # rubygems.org is rate limiting us, slow down.
|
|
78
91
|
@bundle_worker&.stop
|
data/lib/bundler/fetcher.rb
CHANGED
|
@@ -215,7 +215,13 @@ module Bundler
|
|
|
215
215
|
agent << " #{ruby.engine}/#{ruby.versions_string(engine_version)}"
|
|
216
216
|
end
|
|
217
217
|
|
|
218
|
-
|
|
218
|
+
# Some settings keys embed a URI (e.g. `mirror.<uri>`) whose userinfo can
|
|
219
|
+
# carry credentials. Strip the userinfo before advertising the setting
|
|
220
|
+
# names so we don't leak secrets to every gem source we talk to.
|
|
221
|
+
options = Bundler.settings.all.map do |key|
|
|
222
|
+
key.include?("://") ? key.gsub(%r{//[^/@]+@}, "//") : key
|
|
223
|
+
end
|
|
224
|
+
agent << " options/#{options.join(",")}"
|
|
219
225
|
|
|
220
226
|
agent << " ci/#{cis.join(",")}" if cis.any?
|
|
221
227
|
|
|
@@ -243,6 +249,10 @@ module Bundler
|
|
|
243
249
|
fetchers.first.api_fetcher?
|
|
244
250
|
end
|
|
245
251
|
|
|
252
|
+
def release_resolution_memory!
|
|
253
|
+
@fetchers&.each(&:release_resolution_memory!)
|
|
254
|
+
end
|
|
255
|
+
|
|
246
256
|
def gem_remote_fetcher
|
|
247
257
|
@gem_remote_fetcher ||= begin
|
|
248
258
|
require_relative "fetcher/gem_remote_fetcher"
|
|
@@ -314,7 +324,7 @@ module Bundler
|
|
|
314
324
|
if ssl_client_cert
|
|
315
325
|
pem = File.read(ssl_client_cert)
|
|
316
326
|
con.cert = OpenSSL::X509::Certificate.new(pem)
|
|
317
|
-
con.key = OpenSSL::PKey
|
|
327
|
+
con.key = OpenSSL::PKey.read(pem)
|
|
318
328
|
end
|
|
319
329
|
|
|
320
330
|
con.read_timeout = Fetcher.api_timeout
|
data/lib/bundler/gem_helper.rb
CHANGED
|
@@ -32,7 +32,7 @@ module Bundler
|
|
|
32
32
|
|
|
33
33
|
def initialize(base = nil, name = nil)
|
|
34
34
|
@base = File.expand_path(base || SharedHelpers.pwd)
|
|
35
|
-
gemspecs = name ? [File.join(@base, "#{name}.gemspec")] :
|
|
35
|
+
gemspecs = name ? [File.join(@base, "#{name}.gemspec")] : SharedHelpers.glob_files_in_dir("{,*}.gemspec", @base)
|
|
36
36
|
raise "Unable to determine name from existing gemspec. Use :name => 'gemname' in #install_tasks to manually set it." unless gemspecs.size == 1
|
|
37
37
|
@spec_path = gemspecs.first
|
|
38
38
|
@gemspec = Bundler.load_gemspec(@spec_path)
|
|
@@ -124,7 +124,7 @@ module Bundler
|
|
|
124
124
|
end
|
|
125
125
|
|
|
126
126
|
def built_gem_path
|
|
127
|
-
|
|
127
|
+
SharedHelpers.glob_files_in_dir("#{name}-*.gem", base).sort_by {|f| File.mtime(f) }.last
|
|
128
128
|
end
|
|
129
129
|
|
|
130
130
|
def git_push(remote = nil)
|
|
@@ -6,7 +6,7 @@ require_relative "gem_installer"
|
|
|
6
6
|
module Bundler
|
|
7
7
|
class ParallelInstaller
|
|
8
8
|
class SpecInstallation
|
|
9
|
-
attr_accessor :spec, :name, :full_name, :post_install_message, :state, :error
|
|
9
|
+
attr_accessor :spec, :name, :full_name, :post_install_message, :state, :error, :dependencies
|
|
10
10
|
def initialize(spec)
|
|
11
11
|
@spec = spec
|
|
12
12
|
@name = spec.name
|
|
@@ -46,25 +46,11 @@ module Bundler
|
|
|
46
46
|
!post_install_message.empty?
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
dep.type == :development || dep.name == @name
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
# Checks installed dependencies against spec's dependencies to make
|
|
54
|
-
# sure needed dependencies have been installed.
|
|
49
|
+
# Recursively checks that all dependencies (direct and transitive) have been installed.
|
|
55
50
|
def dependencies_installed?(installed_specs)
|
|
56
|
-
dependencies.all?
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
# Represents only the non-development dependencies, the ones that are
|
|
60
|
-
# itself and are in the total list.
|
|
61
|
-
def dependencies
|
|
62
|
-
@dependencies ||= all_dependencies.reject {|dep| ignorable_dependency? dep }
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
# Represents all dependencies
|
|
66
|
-
def all_dependencies
|
|
67
|
-
@spec.dependencies
|
|
51
|
+
dependencies.all? do |dep|
|
|
52
|
+
installed_specs.include?(dep.name) && dep.dependencies_installed?(installed_specs)
|
|
53
|
+
end
|
|
68
54
|
end
|
|
69
55
|
|
|
70
56
|
def to_s
|
|
@@ -85,6 +71,12 @@ module Bundler
|
|
|
85
71
|
@force = force
|
|
86
72
|
@local = local
|
|
87
73
|
@specs = all_specs.map {|s| SpecInstallation.new(s) }
|
|
74
|
+
specs_by_name = @specs.to_h {|s| [s.name, s] }
|
|
75
|
+
@specs.each do |spec_install|
|
|
76
|
+
spec_install.dependencies = spec_install.spec.dependencies.filter_map do |dep|
|
|
77
|
+
specs_by_name[dep.name] unless dep.type == :development || dep.name == spec_install.name
|
|
78
|
+
end
|
|
79
|
+
end
|
|
88
80
|
@specs.each do |spec_install|
|
|
89
81
|
spec_install.state = :installed if skip.include?(spec_install.name)
|
|
90
82
|
end if skip
|
|
@@ -118,10 +110,55 @@ module Bundler
|
|
|
118
110
|
end
|
|
119
111
|
|
|
120
112
|
def install_with_worker
|
|
121
|
-
|
|
122
|
-
|
|
113
|
+
with_jobserver do
|
|
114
|
+
installed_specs = {}
|
|
115
|
+
enqueue_specs(installed_specs)
|
|
123
116
|
|
|
124
|
-
|
|
117
|
+
process_specs(installed_specs) until finished_installing?
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def with_jobserver
|
|
122
|
+
# The jobserver hands tokens to child `make` processes through MAKEFLAGS
|
|
123
|
+
# using the GNU make `--jobserver-auth` protocol. nmake, the default make
|
|
124
|
+
# on mswin, instead reads MAKEFLAGS as bare option letters and aborts
|
|
125
|
+
# every native extension build with `fatal error U1065: invalid option
|
|
126
|
+
# '-'`. Skip the jobserver when nmake is in use. Other Windows toolchains
|
|
127
|
+
# such as mingw use GNU make and keep working through the inherited pipe.
|
|
128
|
+
return yield if nmake? || bsd_make?
|
|
129
|
+
|
|
130
|
+
begin
|
|
131
|
+
r, w = IO.pipe
|
|
132
|
+
r.close_on_exec = false
|
|
133
|
+
w.close_on_exec = false
|
|
134
|
+
w.write("*" * @size)
|
|
135
|
+
|
|
136
|
+
old_makeflags = ENV["MAKEFLAGS"]
|
|
137
|
+
ENV["MAKEFLAGS"] = [old_makeflags, "--jobserver-auth=#{r.fileno},#{w.fileno}"].compact.join(" ")
|
|
138
|
+
|
|
139
|
+
yield
|
|
140
|
+
ensure
|
|
141
|
+
# Restore MAKEFLAGS before closing the pipe so a close failure can't
|
|
142
|
+
# leave the process with descriptors that point at a closed pipe.
|
|
143
|
+
old_makeflags ? ENV["MAKEFLAGS"] = old_makeflags : ENV.delete("MAKEFLAGS")
|
|
144
|
+
|
|
145
|
+
r&.close
|
|
146
|
+
w&.close
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Mirror how RubyGems' extension builder picks the make program so the
|
|
151
|
+
# jobserver is only set up when a GNU-compatible make will consume it.
|
|
152
|
+
def nmake?
|
|
153
|
+
make = ENV["MAKE"] || ENV["make"]
|
|
154
|
+
make ||= "nmake" if RUBY_PLATFORM.include?("mswin")
|
|
155
|
+
/\bnmake/i.match?(make.to_s)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def bsd_make?
|
|
159
|
+
return false unless Gem.freebsd_platform?
|
|
160
|
+
make = ENV["MAKE"] || ENV["make"] || "make"
|
|
161
|
+
!/\bgmake/i.match?(make)
|
|
125
162
|
end
|
|
126
163
|
|
|
127
164
|
def install_serially
|
|
@@ -218,7 +255,15 @@ module Bundler
|
|
|
218
255
|
|
|
219
256
|
def require_tree_for_spec(spec)
|
|
220
257
|
tree = @spec_set.what_required(spec)
|
|
221
|
-
|
|
258
|
+
gemfile_name = begin
|
|
259
|
+
File.basename(SharedHelpers.default_gemfile)
|
|
260
|
+
rescue GemfileNotFound
|
|
261
|
+
# This runs while reporting an install error. When no Gemfile can be
|
|
262
|
+
# located (e.g. Bundler used as a library), raising here would mask
|
|
263
|
+
# the original error, so fall back to a generic header instead.
|
|
264
|
+
"Gemfile"
|
|
265
|
+
end
|
|
266
|
+
t = String.new("In #{gemfile_name}:\n")
|
|
222
267
|
tree.each_with_index do |s, depth|
|
|
223
268
|
t << " " * depth.succ << s.name
|
|
224
269
|
unless tree.last == s
|