removed /etc and /opt

This commit is contained in:
cutemeli
2025-12-22 10:48:14 +00:00
parent 5ce7ca2c5d
commit 10d1afbb17
32559 changed files with 0 additions and 6756692 deletions

View File

@@ -1,182 +0,0 @@
# frozen_string_literal: true
# Include the English library file in a Ruby script, and you can
# reference the global variables such as <tt>$_</tt> using less
# cryptic names, listed below.
#
# Without 'English':
#
# $\ = ' -- '
# "waterbuffalo" =~ /buff/
# print $', $$, "\n"
#
# With English:
#
# require "English"
#
# $OUTPUT_FIELD_SEPARATOR = ' -- '
# "waterbuffalo" =~ /buff/
# print $POSTMATCH, $PID, "\n"
#
# Below is a full list of descriptive aliases and their associated global
# variable:
#
# $ERROR_INFO:: $!
# $ERROR_POSITION:: $@
# $FS:: $;
# $FIELD_SEPARATOR:: $;
# $OFS:: $,
# $OUTPUT_FIELD_SEPARATOR:: $,
# $RS:: $/
# $INPUT_RECORD_SEPARATOR:: $/
# $ORS:: $\
# $OUTPUT_RECORD_SEPARATOR:: $\
# $INPUT_LINE_NUMBER:: $.
# $NR:: $.
# $LAST_READ_LINE:: $_
# $DEFAULT_OUTPUT:: $>
# $DEFAULT_INPUT:: $<
# $PID:: $$
# $PROCESS_ID:: $$
# $CHILD_STATUS:: $?
# $LAST_MATCH_INFO:: $~
# $IGNORECASE:: $=
# $ARGV:: $*
# $MATCH:: $&
# $PREMATCH:: $`
# $POSTMATCH:: $'
# $LAST_PAREN_MATCH:: $+
#
module English end if false
# The exception object passed to +raise+.
alias $ERROR_INFO $!
# The stack backtrace generated by the last
# exception. See Kernel#caller for details. Thread local.
alias $ERROR_POSITION $@
# The default separator pattern used by String#split. May be set from
# the command line using the <tt>-F</tt> flag.
alias $FS $;
# The default separator pattern used by String#split. May be set from
# the command line using the <tt>-F</tt> flag.
alias $FIELD_SEPARATOR $;
# The separator string output between the parameters to methods such
# as Kernel#print and Array#join. Defaults to +nil+, which adds no
# text.
alias $OFS $,
# The separator string output between the parameters to methods such
# as Kernel#print and Array#join. Defaults to +nil+, which adds no
# text.
alias $OUTPUT_FIELD_SEPARATOR $,
# The input record separator (newline by default). This is the value
# that routines such as Kernel#gets use to determine record
# boundaries. If set to +nil+, +gets+ will read the entire file.
alias $RS $/
# The input record separator (newline by default). This is the value
# that routines such as Kernel#gets use to determine record
# boundaries. If set to +nil+, +gets+ will read the entire file.
alias $INPUT_RECORD_SEPARATOR $/
# The string appended to the output of every call to methods such as
# Kernel#print and IO#write. The default value is +nil+.
alias $ORS $\
# The string appended to the output of every call to methods such as
# Kernel#print and IO#write. The default value is +nil+.
alias $OUTPUT_RECORD_SEPARATOR $\
# The number of the last line read from the current input file.
alias $INPUT_LINE_NUMBER $.
# The number of the last line read from the current input file.
alias $NR $.
# The last line read by Kernel#gets or
# Kernel#readline. Many string-related functions in the
# Kernel module operate on <tt>$_</tt> by default. The variable is
# local to the current scope. Thread local.
alias $LAST_READ_LINE $_
# The destination of output for Kernel#print
# and Kernel#printf. The default value is
# <tt>$stdout</tt>.
alias $DEFAULT_OUTPUT $>
# An object that provides access to the concatenation
# of the contents of all the files
# given as command-line arguments, or <tt>$stdin</tt>
# (in the case where there are no
# arguments). <tt>$<</tt> supports methods similar to a
# File object:
# +inmode+, +close+,
# <tt>closed?</tt>, +each+,
# <tt>each_byte</tt>, <tt>each_line</tt>,
# +eof+, <tt>eof?</tt>, +file+,
# +filename+, +fileno+,
# +getc+, +gets+, +lineno+,
# <tt>lineno=</tt>, +path+,
# +pos+, <tt>pos=</tt>,
# +read+, +readchar+,
# +readline+, +readlines+,
# +rewind+, +seek+, +skip+,
# +tell+, <tt>to_a</tt>, <tt>to_i</tt>,
# <tt>to_io</tt>, <tt>to_s</tt>, along with the
# methods in Enumerable. The method +file+
# returns a File object for the file currently
# being read. This may change as <tt>$<</tt> reads
# through the files on the command line. Read only.
alias $DEFAULT_INPUT $<
# The process number of the program being executed. Read only.
alias $PID $$
# The process number of the program being executed. Read only.
alias $PROCESS_ID $$
# The exit status of the last child process to terminate. Read
# only. Thread local.
alias $CHILD_STATUS $?
# A +MatchData+ object that encapsulates the results of a successful
# pattern match. The variables <tt>$&</tt>, <tt>$`</tt>, <tt>$'</tt>,
# and <tt>$1</tt> to <tt>$9</tt> are all derived from
# <tt>$~</tt>. Assigning to <tt>$~</tt> changes the values of these
# derived variables. This variable is local to the current
# scope.
alias $LAST_MATCH_INFO $~
# This variable is no longer effective. Deprecated.
alias $IGNORECASE $=
# An array of strings containing the command-line
# options from the invocation of the program. Options
# used by the Ruby interpreter will have been
# removed. Read only. Also known simply as +ARGV+.
alias $ARGV $*
# The string matched by the last successful pattern
# match. This variable is local to the current
# scope. Read only.
alias $MATCH $&
# The string preceding the match in the last
# successful pattern match. This variable is local to
# the current scope. Read only.
alias $PREMATCH $`
# The string following the match in the last
# successful pattern match. This variable is local to
# the current scope. Read only.
alias $POSTMATCH $'
# The contents of the highest-numbered group matched in the last
# successful pattern match. Thus, in <tt>"cat" =~ /(c|a)(t|z)/</tt>,
# <tt>$+</tt> will be set to "t". This variable is local to the
# current scope. Read only.
alias $LAST_PAREN_MATCH $+

View File

@@ -1,132 +0,0 @@
# frozen_string_literal: true
#--
# Copyright (c) 2001,2003 Akinori MUSHA <knu@iDaemons.org>
#
# All rights reserved. You can redistribute and/or modify it under
# the same terms as Ruby.
#
# $Idaemons: /home/cvs/rb/abbrev.rb,v 1.2 2001/05/30 09:37:45 knu Exp $
# $RoughId: abbrev.rb,v 1.4 2003/10/14 19:45:42 knu Exp $
# $Id$
#++
##
# Calculates the set of unambiguous abbreviations for a given set of strings.
#
# require 'abbrev'
# require 'pp'
#
# pp Abbrev.abbrev(['ruby'])
# #=> {"ruby"=>"ruby", "rub"=>"ruby", "ru"=>"ruby", "r"=>"ruby"}
#
# pp Abbrev.abbrev(%w{ ruby rules })
#
# _Generates:_
# { "ruby" => "ruby",
# "rub" => "ruby",
# "rules" => "rules",
# "rule" => "rules",
# "rul" => "rules" }
#
# It also provides an array core extension, Array#abbrev.
#
# pp %w{ summer winter }.abbrev
#
# _Generates:_
# { "summer" => "summer",
# "summe" => "summer",
# "summ" => "summer",
# "sum" => "summer",
# "su" => "summer",
# "s" => "summer",
# "winter" => "winter",
# "winte" => "winter",
# "wint" => "winter",
# "win" => "winter",
# "wi" => "winter",
# "w" => "winter" }
module Abbrev
# Given a set of strings, calculate the set of unambiguous abbreviations for
# those strings, and return a hash where the keys are all the possible
# abbreviations and the values are the full strings.
#
# Thus, given +words+ is "car" and "cone", the keys pointing to "car" would
# be "ca" and "car", while those pointing to "cone" would be "co", "con", and
# "cone".
#
# require 'abbrev'
#
# Abbrev.abbrev(%w{ car cone })
# #=> {"ca"=>"car", "con"=>"cone", "co"=>"cone", "car"=>"car", "cone"=>"cone"}
#
# The optional +pattern+ parameter is a pattern or a string. Only input
# strings that match the pattern or start with the string are included in the
# output hash.
#
# Abbrev.abbrev(%w{car box cone crab}, /b/)
# #=> {"box"=>"box", "bo"=>"box", "b"=>"box", "crab" => "crab"}
#
# Abbrev.abbrev(%w{car box cone}, 'ca')
# #=> {"car"=>"car", "ca"=>"car"}
def abbrev(words, pattern = nil)
table = {}
seen = Hash.new(0)
if pattern.is_a?(String)
pattern = /\A#{Regexp.quote(pattern)}/ # regard as a prefix
end
words.each do |word|
next if word.empty?
word.size.downto(1) { |len|
abbrev = word[0...len]
next if pattern && pattern !~ abbrev
case seen[abbrev] += 1
when 1
table[abbrev] = word
when 2
table.delete(abbrev)
else
break
end
}
end
words.each do |word|
next if pattern && pattern !~ word
table[word] = word
end
table
end
module_function :abbrev
end
class Array
# Calculates the set of unambiguous abbreviations for the strings in +self+.
#
# require 'abbrev'
# %w{ car cone }.abbrev
# #=> {"car"=>"car", "ca"=>"car", "cone"=>"cone", "con"=>"cone", "co"=>"cone"}
#
# The optional +pattern+ parameter is a pattern or a string. Only input
# strings that match the pattern or start with the string are included in the
# output hash.
#
# %w{ fast boat day }.abbrev(/^.a/)
# #=> {"fast"=>"fast", "fas"=>"fast", "fa"=>"fast", "day"=>"day", "da"=>"day"}
#
# Abbrev.abbrev(%w{car box cone}, "ca")
# #=> {"car"=>"car", "ca"=>"car"}
#
# See also Abbrev.abbrev
def abbrev(pattern = nil)
Abbrev::abbrev(self, pattern)
end
end

View File

@@ -1,110 +0,0 @@
# frozen_string_literal: true
#
# = base64.rb: methods for base64-encoding and -decoding strings
#
# The Base64 module provides for the encoding (#encode64, #strict_encode64,
# #urlsafe_encode64) and decoding (#decode64, #strict_decode64,
# #urlsafe_decode64) of binary data using a Base64 representation.
#
# == Example
#
# A simple encoding and decoding.
#
# require "base64"
#
# enc = Base64.encode64('Send reinforcements')
# # -> "U2VuZCByZWluZm9yY2VtZW50cw==\n"
# plain = Base64.decode64(enc)
# # -> "Send reinforcements"
#
# The purpose of using base64 to encode data is that it translates any
# binary data into purely printable characters.
module Base64
module_function
# Returns the Base64-encoded version of +bin+.
# This method complies with RFC 2045.
# Line feeds are added to every 60 encoded characters.
#
# require 'base64'
# Base64.encode64("Now is the time for all good coders\nto learn Ruby")
#
# <i>Generates:</i>
#
# Tm93IGlzIHRoZSB0aW1lIGZvciBhbGwgZ29vZCBjb2RlcnMKdG8gbGVhcm4g
# UnVieQ==
def encode64(bin)
[bin].pack("m")
end
# Returns the Base64-decoded version of +str+.
# This method complies with RFC 2045.
# Characters outside the base alphabet are ignored.
#
# require 'base64'
# str = 'VGhpcyBpcyBsaW5lIG9uZQpUaGlzIG' +
# 'lzIGxpbmUgdHdvClRoaXMgaXMgbGlu' +
# 'ZSB0aHJlZQpBbmQgc28gb24uLi4K'
# puts Base64.decode64(str)
#
# <i>Generates:</i>
#
# This is line one
# This is line two
# This is line three
# And so on...
def decode64(str)
str.unpack1("m")
end
# Returns the Base64-encoded version of +bin+.
# This method complies with RFC 4648.
# No line feeds are added.
def strict_encode64(bin)
[bin].pack("m0")
end
# Returns the Base64-decoded version of +str+.
# This method complies with RFC 4648.
# ArgumentError is raised if +str+ is incorrectly padded or contains
# non-alphabet characters. Note that CR or LF are also rejected.
def strict_decode64(str)
str.unpack1("m0")
end
# Returns the Base64-encoded version of +bin+.
# This method complies with ``Base 64 Encoding with URL and Filename Safe
# Alphabet'' in RFC 4648.
# The alphabet uses '-' instead of '+' and '_' instead of '/'.
# Note that the result can still contain '='.
# You can remove the padding by setting +padding+ as false.
def urlsafe_encode64(bin, padding: true)
str = strict_encode64(bin)
str.chomp!("==") or str.chomp!("=") unless padding
str.tr!("+/", "-_")
str
end
# Returns the Base64-decoded version of +str+.
# This method complies with ``Base 64 Encoding with URL and Filename Safe
# Alphabet'' in RFC 4648.
# The alphabet uses '-' instead of '+' and '_' instead of '/'.
#
# The padding character is optional.
# This method accepts both correctly-padded and unpadded input.
# Note that it still rejects incorrectly-padded input.
def urlsafe_decode64(str)
# NOTE: RFC 4648 does say nothing about unpadded input, but says that
# "the excess pad characters MAY also be ignored", so it is inferred that
# unpadded input is also acceptable.
if !str.end_with?("=") && str.length % 4 != 0
str = str.ljust((str.length + 3) & ~3, "=")
str.tr!("-_", "+/")
else
str = str.tr("-_", "+/")
end
strict_decode64(str)
end
end

View File

@@ -1,582 +0,0 @@
# frozen_string_literal: true
#--
# benchmark.rb - a performance benchmarking library
#
# $Id$
#
# Created by Gotoken (gotoken@notwork.org).
#
# Documentation by Gotoken (original RD), Lyle Johnson (RDoc conversion), and
# Gavin Sinclair (editing).
#++
#
# == Overview
#
# The Benchmark module provides methods for benchmarking Ruby code, giving
# detailed reports on the time taken for each task.
#
# The Benchmark module provides methods to measure and report the time
# used to execute Ruby code.
#
# * Measure the time to construct the string given by the expression
# <code>"a"*1_000_000_000</code>:
#
# require 'benchmark'
#
# puts Benchmark.measure { "a"*1_000_000_000 }
#
# On my machine (OSX 10.8.3 on i5 1.7 GHz) this generates:
#
# 0.350000 0.400000 0.750000 ( 0.835234)
#
# This report shows the user CPU time, system CPU time, the sum of
# the user and system CPU times, and the elapsed real time. The unit
# of time is seconds.
#
# * Do some experiments sequentially using the #bm method:
#
# require 'benchmark'
#
# n = 5000000
# Benchmark.bm do |x|
# x.report { for i in 1..n; a = "1"; end }
# x.report { n.times do ; a = "1"; end }
# x.report { 1.upto(n) do ; a = "1"; end }
# end
#
# The result:
#
# user system total real
# 1.010000 0.000000 1.010000 ( 1.014479)
# 1.000000 0.000000 1.000000 ( 0.998261)
# 0.980000 0.000000 0.980000 ( 0.981335)
#
# * Continuing the previous example, put a label in each report:
#
# require 'benchmark'
#
# n = 5000000
# Benchmark.bm(7) do |x|
# x.report("for:") { for i in 1..n; a = "1"; end }
# x.report("times:") { n.times do ; a = "1"; end }
# x.report("upto:") { 1.upto(n) do ; a = "1"; end }
# end
#
# The result:
#
# user system total real
# for: 1.010000 0.000000 1.010000 ( 1.015688)
# times: 1.000000 0.000000 1.000000 ( 1.003611)
# upto: 1.030000 0.000000 1.030000 ( 1.028098)
#
# * The times for some benchmarks depend on the order in which items
# are run. These differences are due to the cost of memory
# allocation and garbage collection. To avoid these discrepancies,
# the #bmbm method is provided. For example, to compare ways to
# sort an array of floats:
#
# require 'benchmark'
#
# array = (1..1000000).map { rand }
#
# Benchmark.bmbm do |x|
# x.report("sort!") { array.dup.sort! }
# x.report("sort") { array.dup.sort }
# end
#
# The result:
#
# Rehearsal -----------------------------------------
# sort! 1.490000 0.010000 1.500000 ( 1.490520)
# sort 1.460000 0.000000 1.460000 ( 1.463025)
# -------------------------------- total: 2.960000sec
#
# user system total real
# sort! 1.460000 0.000000 1.460000 ( 1.460465)
# sort 1.450000 0.010000 1.460000 ( 1.448327)
#
# * Report statistics of sequential experiments with unique labels,
# using the #benchmark method:
#
# require 'benchmark'
# include Benchmark # we need the CAPTION and FORMAT constants
#
# n = 5000000
# Benchmark.benchmark(CAPTION, 7, FORMAT, ">total:", ">avg:") do |x|
# tf = x.report("for:") { for i in 1..n; a = "1"; end }
# tt = x.report("times:") { n.times do ; a = "1"; end }
# tu = x.report("upto:") { 1.upto(n) do ; a = "1"; end }
# [tf+tt+tu, (tf+tt+tu)/3]
# end
#
# The result:
#
# user system total real
# for: 0.950000 0.000000 0.950000 ( 0.952039)
# times: 0.980000 0.000000 0.980000 ( 0.984938)
# upto: 0.950000 0.000000 0.950000 ( 0.946787)
# >total: 2.880000 0.000000 2.880000 ( 2.883764)
# >avg: 0.960000 0.000000 0.960000 ( 0.961255)
module Benchmark
BENCHMARK_VERSION = "2002-04-25" # :nodoc:
# Invokes the block with a Benchmark::Report object, which
# may be used to collect and report on the results of individual
# benchmark tests. Reserves +label_width+ leading spaces for
# labels on each line. Prints +caption+ at the top of the
# report, and uses +format+ to format each line.
# (Note: +caption+ must contain a terminating newline character,
# see the default Benchmark::Tms::CAPTION for an example.)
#
# Returns an array of Benchmark::Tms objects.
#
# If the block returns an array of
# Benchmark::Tms objects, these will be used to format
# additional lines of output. If +labels+ parameter are
# given, these are used to label these extra lines.
#
# _Note_: Other methods provide a simpler interface to this one, and are
# suitable for nearly all benchmarking requirements. See the examples in
# Benchmark, and the #bm and #bmbm methods.
#
# Example:
#
# require 'benchmark'
# include Benchmark # we need the CAPTION and FORMAT constants
#
# n = 5000000
# Benchmark.benchmark(CAPTION, 7, FORMAT, ">total:", ">avg:") do |x|
# tf = x.report("for:") { for i in 1..n; a = "1"; end }
# tt = x.report("times:") { n.times do ; a = "1"; end }
# tu = x.report("upto:") { 1.upto(n) do ; a = "1"; end }
# [tf+tt+tu, (tf+tt+tu)/3]
# end
#
# Generates:
#
# user system total real
# for: 0.970000 0.000000 0.970000 ( 0.970493)
# times: 0.990000 0.000000 0.990000 ( 0.989542)
# upto: 0.970000 0.000000 0.970000 ( 0.972854)
# >total: 2.930000 0.000000 2.930000 ( 2.932889)
# >avg: 0.976667 0.000000 0.976667 ( 0.977630)
#
def benchmark(caption = "", label_width = nil, format = nil, *labels) # :yield: report
sync = $stdout.sync
$stdout.sync = true
label_width ||= 0
label_width += 1
format ||= FORMAT
print ' '*label_width + caption unless caption.empty?
report = Report.new(label_width, format)
results = yield(report)
Array === results and results.grep(Tms).each {|t|
print((labels.shift || t.label || "").ljust(label_width), t.format(format))
}
report.list
ensure
$stdout.sync = sync unless sync.nil?
end
# A simple interface to the #benchmark method, #bm generates sequential
# reports with labels. +label_width+ and +labels+ parameters have the same
# meaning as for #benchmark.
#
# require 'benchmark'
#
# n = 5000000
# Benchmark.bm(7) do |x|
# x.report("for:") { for i in 1..n; a = "1"; end }
# x.report("times:") { n.times do ; a = "1"; end }
# x.report("upto:") { 1.upto(n) do ; a = "1"; end }
# end
#
# Generates:
#
# user system total real
# for: 0.960000 0.000000 0.960000 ( 0.957966)
# times: 0.960000 0.000000 0.960000 ( 0.960423)
# upto: 0.950000 0.000000 0.950000 ( 0.954864)
#
def bm(label_width = 0, *labels, &blk) # :yield: report
benchmark(CAPTION, label_width, FORMAT, *labels, &blk)
end
# Sometimes benchmark results are skewed because code executed
# earlier encounters different garbage collection overheads than
# that run later. #bmbm attempts to minimize this effect by running
# the tests twice, the first time as a rehearsal in order to get the
# runtime environment stable, the second time for
# real. GC.start is executed before the start of each of
# the real timings; the cost of this is not included in the
# timings. In reality, though, there's only so much that #bmbm can
# do, and the results are not guaranteed to be isolated from garbage
# collection and other effects.
#
# Because #bmbm takes two passes through the tests, it can
# calculate the required label width.
#
# require 'benchmark'
#
# array = (1..1000000).map { rand }
#
# Benchmark.bmbm do |x|
# x.report("sort!") { array.dup.sort! }
# x.report("sort") { array.dup.sort }
# end
#
# Generates:
#
# Rehearsal -----------------------------------------
# sort! 1.440000 0.010000 1.450000 ( 1.446833)
# sort 1.440000 0.000000 1.440000 ( 1.448257)
# -------------------------------- total: 2.890000sec
#
# user system total real
# sort! 1.460000 0.000000 1.460000 ( 1.458065)
# sort 1.450000 0.000000 1.450000 ( 1.455963)
#
# #bmbm yields a Benchmark::Job object and returns an array of
# Benchmark::Tms objects.
#
def bmbm(width = 0) # :yield: job
job = Job.new(width)
yield(job)
width = job.width + 1
sync = $stdout.sync
$stdout.sync = true
# rehearsal
puts 'Rehearsal '.ljust(width+CAPTION.length,'-')
ets = job.list.inject(Tms.new) { |sum,(label,item)|
print label.ljust(width)
res = Benchmark.measure(&item)
print res.format
sum + res
}.format("total: %tsec")
print " #{ets}\n\n".rjust(width+CAPTION.length+2,'-')
# take
print ' '*width + CAPTION
job.list.map { |label,item|
GC.start
print label.ljust(width)
Benchmark.measure(label, &item).tap { |res| print res }
}
ensure
$stdout.sync = sync unless sync.nil?
end
#
# Returns the time used to execute the given block as a
# Benchmark::Tms object. Takes +label+ option.
#
# require 'benchmark'
#
# n = 1000000
#
# time = Benchmark.measure do
# n.times { a = "1" }
# end
# puts time
#
# Generates:
#
# 0.220000 0.000000 0.220000 ( 0.227313)
#
def measure(label = "") # :yield:
t0, r0 = Process.times, Process.clock_gettime(Process::CLOCK_MONOTONIC)
yield
t1, r1 = Process.times, Process.clock_gettime(Process::CLOCK_MONOTONIC)
Benchmark::Tms.new(t1.utime - t0.utime,
t1.stime - t0.stime,
t1.cutime - t0.cutime,
t1.cstime - t0.cstime,
r1 - r0,
label)
end
#
# Returns the elapsed real time used to execute the given block.
#
def realtime # :yield:
r0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
yield
Process.clock_gettime(Process::CLOCK_MONOTONIC) - r0
end
module_function :benchmark, :measure, :realtime, :bm, :bmbm
#
# A Job is a sequence of labelled blocks to be processed by the
# Benchmark.bmbm method. It is of little direct interest to the user.
#
class Job # :nodoc:
#
# Returns an initialized Job instance.
# Usually, one doesn't call this method directly, as new
# Job objects are created by the #bmbm method.
# +width+ is a initial value for the label offset used in formatting;
# the #bmbm method passes its +width+ argument to this constructor.
#
def initialize(width)
@width = width
@list = []
end
#
# Registers the given label and block pair in the job list.
#
def item(label = "", &blk) # :yield:
raise ArgumentError, "no block" unless block_given?
label = label.to_s
w = label.length
@width = w if @width < w
@list << [label, blk]
self
end
alias report item
# An array of 2-element arrays, consisting of label and block pairs.
attr_reader :list
# Length of the widest label in the #list.
attr_reader :width
end
#
# This class is used by the Benchmark.benchmark and Benchmark.bm methods.
# It is of little direct interest to the user.
#
class Report # :nodoc:
#
# Returns an initialized Report instance.
# Usually, one doesn't call this method directly, as new
# Report objects are created by the #benchmark and #bm methods.
# +width+ and +format+ are the label offset and
# format string used by Tms#format.
#
def initialize(width = 0, format = nil)
@width, @format, @list = width, format, []
end
#
# Prints the +label+ and measured time for the block,
# formatted by +format+. See Tms#format for the
# formatting rules.
#
def item(label = "", *format, &blk) # :yield:
print label.to_s.ljust(@width)
@list << res = Benchmark.measure(label, &blk)
print res.format(@format, *format)
res
end
alias report item
# An array of Benchmark::Tms objects representing each item.
attr_reader :list
end
#
# A data object, representing the times associated with a benchmark
# measurement.
#
class Tms
# Default caption, see also Benchmark::CAPTION
CAPTION = " user system total real\n"
# Default format string, see also Benchmark::FORMAT
FORMAT = "%10.6u %10.6y %10.6t %10.6r\n"
# User CPU time
attr_reader :utime
# System CPU time
attr_reader :stime
# User CPU time of children
attr_reader :cutime
# System CPU time of children
attr_reader :cstime
# Elapsed real time
attr_reader :real
# Total time, that is +utime+ + +stime+ + +cutime+ + +cstime+
attr_reader :total
# Label
attr_reader :label
#
# Returns an initialized Tms object which has
# +utime+ as the user CPU time, +stime+ as the system CPU time,
# +cutime+ as the children's user CPU time, +cstime+ as the children's
# system CPU time, +real+ as the elapsed real time and +label+ as the label.
#
def initialize(utime = 0.0, stime = 0.0, cutime = 0.0, cstime = 0.0, real = 0.0, label = nil)
@utime, @stime, @cutime, @cstime, @real, @label = utime, stime, cutime, cstime, real, label.to_s
@total = @utime + @stime + @cutime + @cstime
end
#
# Returns a new Tms object whose times are the sum of the times for this
# Tms object, plus the time required to execute the code block (+blk+).
#
def add(&blk) # :yield:
self + Benchmark.measure(&blk)
end
#
# An in-place version of #add.
# Changes the times of this Tms object by making it the sum of the times
# for this Tms object, plus the time required to execute
# the code block (+blk+).
#
def add!(&blk)
t = Benchmark.measure(&blk)
@utime = utime + t.utime
@stime = stime + t.stime
@cutime = cutime + t.cutime
@cstime = cstime + t.cstime
@real = real + t.real
self
end
#
# Returns a new Tms object obtained by memberwise summation
# of the individual times for this Tms object with those of the +other+
# Tms object.
# This method and #/() are useful for taking statistics.
#
def +(other); memberwise(:+, other) end
#
# Returns a new Tms object obtained by memberwise subtraction
# of the individual times for the +other+ Tms object from those of this
# Tms object.
#
def -(other); memberwise(:-, other) end
#
# Returns a new Tms object obtained by memberwise multiplication
# of the individual times for this Tms object by +x+.
#
def *(x); memberwise(:*, x) end
#
# Returns a new Tms object obtained by memberwise division
# of the individual times for this Tms object by +x+.
# This method and #+() are useful for taking statistics.
#
def /(x); memberwise(:/, x) end
#
# Returns the contents of this Tms object as
# a formatted string, according to a +format+ string
# like that passed to Kernel.format. In addition, #format
# accepts the following extensions:
#
# <tt>%u</tt>:: Replaced by the user CPU time, as reported by Tms#utime.
# <tt>%y</tt>:: Replaced by the system CPU time, as reported by #stime (Mnemonic: y of "s*y*stem")
# <tt>%U</tt>:: Replaced by the children's user CPU time, as reported by Tms#cutime
# <tt>%Y</tt>:: Replaced by the children's system CPU time, as reported by Tms#cstime
# <tt>%t</tt>:: Replaced by the total CPU time, as reported by Tms#total
# <tt>%r</tt>:: Replaced by the elapsed real time, as reported by Tms#real
# <tt>%n</tt>:: Replaced by the label string, as reported by Tms#label (Mnemonic: n of "*n*ame")
#
# If +format+ is not given, FORMAT is used as default value, detailing the
# user, system and real elapsed time.
#
def format(format = nil, *args)
str = (format || FORMAT).dup
str.gsub!(/(%[-+.\d]*)n/) { "#{$1}s" % label }
str.gsub!(/(%[-+.\d]*)u/) { "#{$1}f" % utime }
str.gsub!(/(%[-+.\d]*)y/) { "#{$1}f" % stime }
str.gsub!(/(%[-+.\d]*)U/) { "#{$1}f" % cutime }
str.gsub!(/(%[-+.\d]*)Y/) { "#{$1}f" % cstime }
str.gsub!(/(%[-+.\d]*)t/) { "#{$1}f" % total }
str.gsub!(/(%[-+.\d]*)r/) { "(#{$1}f)" % real }
format ? str % args : str
end
#
# Same as #format.
#
def to_s
format
end
#
# Returns a new 6-element array, consisting of the
# label, user CPU time, system CPU time, children's
# user CPU time, children's system CPU time and elapsed
# real time.
#
def to_a
[@label, @utime, @stime, @cutime, @cstime, @real]
end
#
# Returns a hash containing the same data as `to_a`.
#
def to_h
{
label: @label,
utime: @utime,
stime: @stime,
cutime: @cutime,
cstime: @cstime,
real: @real
}
end
protected
#
# Returns a new Tms object obtained by memberwise operation +op+
# of the individual times for this Tms object with those of the other
# Tms object (+x+).
#
# +op+ can be a mathematical operation such as <tt>+</tt>, <tt>-</tt>,
# <tt>*</tt>, <tt>/</tt>
#
def memberwise(op, x)
case x
when Benchmark::Tms
Benchmark::Tms.new(utime.__send__(op, x.utime),
stime.__send__(op, x.stime),
cutime.__send__(op, x.cutime),
cstime.__send__(op, x.cstime),
real.__send__(op, x.real)
)
else
Benchmark::Tms.new(utime.__send__(op, x),
stime.__send__(op, x),
cutime.__send__(op, x),
cstime.__send__(op, x),
real.__send__(op, x)
)
end
end
end
# The default caption string (heading above the output times).
CAPTION = Benchmark::Tms::CAPTION
# The default format string used to display times. See also Benchmark::Tms#format.
FORMAT = Benchmark::Tms::FORMAT
end

View File

@@ -1,4 +0,0 @@
# frozen_string_literal: true
module Benchmark
VERSION = "0.2.1"
end

View File

@@ -1 +0,0 @@
require 'bigdecimal.so'

View File

@@ -1,90 +0,0 @@
# frozen_string_literal: false
require 'bigdecimal'
# require 'bigdecimal/jacobian'
#
# Provides methods to compute the Jacobian matrix of a set of equations at a
# point x. In the methods below:
#
# f is an Object which is used to compute the Jacobian matrix of the equations.
# It must provide the following methods:
#
# f.values(x):: returns the values of all functions at x
#
# f.zero:: returns 0.0
# f.one:: returns 1.0
# f.two:: returns 2.0
# f.ten:: returns 10.0
#
# f.eps:: returns the convergence criterion (epsilon value) used to determine whether two values are considered equal. If |a-b| < epsilon, the two values are considered equal.
#
# x is the point at which to compute the Jacobian.
#
# fx is f.values(x).
#
module Jacobian
module_function
# Determines the equality of two numbers by comparing to zero, or using the epsilon value
def isEqual(a,b,zero=0.0,e=1.0e-8)
aa = a.abs
bb = b.abs
if aa == zero && bb == zero then
true
else
if ((a-b)/(aa+bb)).abs < e then
true
else
false
end
end
end
# Computes the derivative of +f[i]+ at +x[i]+.
# +fx+ is the value of +f+ at +x+.
def dfdxi(f,fx,x,i)
nRetry = 0
n = x.size
xSave = x[i]
ok = 0
ratio = f.ten*f.ten*f.ten
dx = x[i].abs/ratio
dx = fx[i].abs/ratio if isEqual(dx,f.zero,f.zero,f.eps)
dx = f.one/f.ten if isEqual(dx,f.zero,f.zero,f.eps)
until ok>0 do
deriv = []
nRetry += 1
if nRetry > 100
raise "Singular Jacobian matrix. No change at x[" + i.to_s + "]"
end
dx = dx*f.two
x[i] += dx
fxNew = f.values(x)
for j in 0...n do
if !isEqual(fxNew[j],fx[j],f.zero,f.eps) then
ok += 1
deriv <<= (fxNew[j]-fx[j])/dx
else
deriv <<= f.zero
end
end
x[i] = xSave
end
deriv
end
# Computes the Jacobian of +f+ at +x+. +fx+ is the value of +f+ at +x+.
def jacobian(f,fx,x)
n = x.size
dfdx = Array.new(n*n)
for i in 0...n do
df = dfdxi(f,fx,x,i)
for j in 0...n do
dfdx[j*n+i] = df[j]
end
end
dfdx
end
end

View File

@@ -1,89 +0,0 @@
# frozen_string_literal: false
require 'bigdecimal'
#
# Solves a*x = b for x, using LU decomposition.
#
module LUSolve
module_function
# Performs LU decomposition of the n by n matrix a.
def ludecomp(a,n,zero=0,one=1)
prec = BigDecimal.limit(nil)
ps = []
scales = []
for i in 0...n do # pick up largest(abs. val.) element in each row.
ps <<= i
nrmrow = zero
ixn = i*n
for j in 0...n do
biggst = a[ixn+j].abs
nrmrow = biggst if biggst>nrmrow
end
if nrmrow>zero then
scales <<= one.div(nrmrow,prec)
else
raise "Singular matrix"
end
end
n1 = n - 1
for k in 0...n1 do # Gaussian elimination with partial pivoting.
biggst = zero;
for i in k...n do
size = a[ps[i]*n+k].abs*scales[ps[i]]
if size>biggst then
biggst = size
pividx = i
end
end
raise "Singular matrix" if biggst<=zero
if pividx!=k then
j = ps[k]
ps[k] = ps[pividx]
ps[pividx] = j
end
pivot = a[ps[k]*n+k]
for i in (k+1)...n do
psin = ps[i]*n
a[psin+k] = mult = a[psin+k].div(pivot,prec)
if mult!=zero then
pskn = ps[k]*n
for j in (k+1)...n do
a[psin+j] -= mult.mult(a[pskn+j],prec)
end
end
end
end
raise "Singular matrix" if a[ps[n1]*n+n1] == zero
ps
end
# Solves a*x = b for x, using LU decomposition.
#
# a is a matrix, b is a constant vector, x is the solution vector.
#
# ps is the pivot, a vector which indicates the permutation of rows performed
# during LU decomposition.
def lusolve(a,b,ps,zero=0.0)
prec = BigDecimal.limit(nil)
n = ps.size
x = []
for i in 0...n do
dot = zero
psin = ps[i]*n
for j in 0...i do
dot = a[psin+j].mult(x[j],prec) + dot
end
x <<= b[ps[i]] - dot
end
(n-1).downto(0) do |i|
dot = zero
psin = ps[i]*n
for j in (i+1)...n do
dot = a[psin+j].mult(x[j],prec) + dot
end
x[i] = (x[i]-dot).div(a[psin+i],prec)
end
x
end
end

View File

@@ -1,232 +0,0 @@
# frozen_string_literal: false
require 'bigdecimal'
#
#--
# Contents:
# sqrt(x, prec)
# sin (x, prec)
# cos (x, prec)
# atan(x, prec) Note: |x|<1, x=0.9999 may not converge.
# PI (prec)
# E (prec) == exp(1.0,prec)
#
# where:
# x ... BigDecimal number to be computed.
# |x| must be small enough to get convergence.
# prec ... Number of digits to be obtained.
#++
#
# Provides mathematical functions.
#
# Example:
#
# require "bigdecimal/math"
#
# include BigMath
#
# a = BigDecimal((PI(100)/2).to_s)
# puts sin(a,100) # => 0.99999999999999999999......e0
#
module BigMath
module_function
# call-seq:
# sqrt(decimal, numeric) -> BigDecimal
#
# Computes the square root of +decimal+ to the specified number of digits of
# precision, +numeric+.
#
# BigMath.sqrt(BigDecimal('2'), 16).to_s
# #=> "0.1414213562373095048801688724e1"
#
def sqrt(x, prec)
x.sqrt(prec)
end
# call-seq:
# sin(decimal, numeric) -> BigDecimal
#
# Computes the sine of +decimal+ to the specified number of digits of
# precision, +numeric+.
#
# If +decimal+ is Infinity or NaN, returns NaN.
#
# BigMath.sin(BigMath.PI(5)/4, 5).to_s
# #=> "0.70710678118654752440082036563292800375e0"
#
def sin(x, prec)
raise ArgumentError, "Zero or negative precision for sin" if prec <= 0
return BigDecimal("NaN") if x.infinite? || x.nan?
n = prec + BigDecimal.double_fig
one = BigDecimal("1")
two = BigDecimal("2")
x = -x if neg = x < 0
if x > (twopi = two * BigMath.PI(prec))
if x > 30
x %= twopi
else
x -= twopi while x > twopi
end
end
x1 = x
x2 = x.mult(x,n)
sign = 1
y = x
d = y
i = one
z = one
while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
m = BigDecimal.double_fig if m < BigDecimal.double_fig
sign = -sign
x1 = x2.mult(x1,n)
i += two
z *= (i-one) * i
d = sign * x1.div(z,m)
y += d
end
neg ? -y : y
end
# call-seq:
# cos(decimal, numeric) -> BigDecimal
#
# Computes the cosine of +decimal+ to the specified number of digits of
# precision, +numeric+.
#
# If +decimal+ is Infinity or NaN, returns NaN.
#
# BigMath.cos(BigMath.PI(4), 16).to_s
# #=> "-0.999999999999999999999999999999856613163740061349e0"
#
def cos(x, prec)
raise ArgumentError, "Zero or negative precision for cos" if prec <= 0
return BigDecimal("NaN") if x.infinite? || x.nan?
n = prec + BigDecimal.double_fig
one = BigDecimal("1")
two = BigDecimal("2")
x = -x if x < 0
if x > (twopi = two * BigMath.PI(prec))
if x > 30
x %= twopi
else
x -= twopi while x > twopi
end
end
x1 = one
x2 = x.mult(x,n)
sign = 1
y = one
d = y
i = BigDecimal("0")
z = one
while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
m = BigDecimal.double_fig if m < BigDecimal.double_fig
sign = -sign
x1 = x2.mult(x1,n)
i += two
z *= (i-one) * i
d = sign * x1.div(z,m)
y += d
end
y
end
# call-seq:
# atan(decimal, numeric) -> BigDecimal
#
# Computes the arctangent of +decimal+ to the specified number of digits of
# precision, +numeric+.
#
# If +decimal+ is NaN, returns NaN.
#
# BigMath.atan(BigDecimal('-1'), 16).to_s
# #=> "-0.785398163397448309615660845819878471907514682065e0"
#
def atan(x, prec)
raise ArgumentError, "Zero or negative precision for atan" if prec <= 0
return BigDecimal("NaN") if x.nan?
pi = PI(prec)
x = -x if neg = x < 0
return pi.div(neg ? -2 : 2, prec) if x.infinite?
return pi / (neg ? -4 : 4) if x.round(prec) == 1
x = BigDecimal("1").div(x, prec) if inv = x > 1
x = (-1 + sqrt(1 + x**2, prec))/x if dbl = x > 0.5
n = prec + BigDecimal.double_fig
y = x
d = y
t = x
r = BigDecimal("3")
x2 = x.mult(x,n)
while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
m = BigDecimal.double_fig if m < BigDecimal.double_fig
t = -t.mult(x2,n)
d = t.div(r,m)
y += d
r += 2
end
y *= 2 if dbl
y = pi / 2 - y if inv
y = -y if neg
y
end
# call-seq:
# PI(numeric) -> BigDecimal
#
# Computes the value of pi to the specified number of digits of precision,
# +numeric+.
#
# BigMath.PI(10).to_s
# #=> "0.3141592653589793238462643388813853786957412e1"
#
def PI(prec)
raise ArgumentError, "Zero or negative precision for PI" if prec <= 0
n = prec + BigDecimal.double_fig
zero = BigDecimal("0")
one = BigDecimal("1")
two = BigDecimal("2")
m25 = BigDecimal("-0.04")
m57121 = BigDecimal("-57121")
pi = zero
d = one
k = one
t = BigDecimal("-80")
while d.nonzero? && ((m = n - (pi.exponent - d.exponent).abs) > 0)
m = BigDecimal.double_fig if m < BigDecimal.double_fig
t = t*m25
d = t.div(k,m)
k = k+two
pi = pi + d
end
d = one
k = one
t = BigDecimal("956")
while d.nonzero? && ((m = n - (pi.exponent - d.exponent).abs) > 0)
m = BigDecimal.double_fig if m < BigDecimal.double_fig
t = t.div(m57121,n)
d = t.div(k,m)
pi = pi + d
k = k+two
end
pi
end
# call-seq:
# E(numeric) -> BigDecimal
#
# Computes e (the base of natural logarithms) to the specified number of
# digits of precision, +numeric+.
#
# BigMath.E(10).to_s
# #=> "0.271828182845904523536028752390026306410273e1"
#
def E(prec)
raise ArgumentError, "Zero or negative precision for E" if prec <= 0
BigMath.exp(1, prec)
end
end

View File

@@ -1,80 +0,0 @@
# frozen_string_literal: false
require "bigdecimal/ludcmp"
require "bigdecimal/jacobian"
#
# newton.rb
#
# Solves the nonlinear algebraic equation system f = 0 by Newton's method.
# This program is not dependent on BigDecimal.
#
# To call:
# n = nlsolve(f,x)
# where n is the number of iterations required,
# x is the initial value vector
# f is an Object which is used to compute the values of the equations to be solved.
# It must provide the following methods:
#
# f.values(x):: returns the values of all functions at x
#
# f.zero:: returns 0.0
# f.one:: returns 1.0
# f.two:: returns 2.0
# f.ten:: returns 10.0
#
# f.eps:: returns the convergence criterion (epsilon value) used to determine whether two values are considered equal. If |a-b| < epsilon, the two values are considered equal.
#
# On exit, x is the solution vector.
#
module Newton
include LUSolve
include Jacobian
module_function
def norm(fv,zero=0.0) # :nodoc:
s = zero
n = fv.size
for i in 0...n do
s += fv[i]*fv[i]
end
s
end
# See also Newton
def nlsolve(f,x)
nRetry = 0
n = x.size
f0 = f.values(x)
zero = f.zero
one = f.one
two = f.two
p5 = one/two
d = norm(f0,zero)
minfact = f.ten*f.ten*f.ten
minfact = one/minfact
e = f.eps
while d >= e do
nRetry += 1
# Not yet converged. => Compute Jacobian matrix
dfdx = jacobian(f,f0,x)
# Solve dfdx*dx = -f0 to estimate dx
dx = lusolve(dfdx,f0,ludecomp(dfdx,n,zero,one),zero)
fact = two
xs = x.dup
begin
fact *= p5
if fact < minfact then
raise "Failed to reduce function values."
end
for i in 0...n do
x[i] = xs[i] - dx[i]*fact
end
f0 = f.values(x)
dn = norm(f0,zero)
end while(dn>=d)
d = dn
end
nRetry
end
end

View File

@@ -1,185 +0,0 @@
# frozen_string_literal: false
#
#--
# bigdecimal/util extends various native classes to provide the #to_d method,
# and provides BigDecimal#to_d and BigDecimal#to_digits.
#++
require 'bigdecimal'
class Integer < Numeric
# call-seq:
# int.to_d -> bigdecimal
#
# Returns the value of +int+ as a BigDecimal.
#
# require 'bigdecimal'
# require 'bigdecimal/util'
#
# 42.to_d # => 0.42e2
#
# See also BigDecimal::new.
#
def to_d
BigDecimal(self)
end
end
class Float < Numeric
# call-seq:
# float.to_d -> bigdecimal
# float.to_d(precision) -> bigdecimal
#
# Returns the value of +float+ as a BigDecimal.
# The +precision+ parameter is used to determine the number of
# significant digits for the result. When +precision+ is set to +0+,
# the number of digits to represent the float being converted is determined
# automatically.
# The default +precision+ is +0+.
#
# require 'bigdecimal'
# require 'bigdecimal/util'
#
# 0.5.to_d # => 0.5e0
# 1.234.to_d # => 0.1234e1
# 1.234.to_d(2) # => 0.12e1
#
# See also BigDecimal::new.
#
def to_d(precision=0)
BigDecimal(self, precision)
end
end
class String
# call-seq:
# str.to_d -> bigdecimal
#
# Returns the result of interpreting leading characters in +str+
# as a BigDecimal.
#
# require 'bigdecimal'
# require 'bigdecimal/util'
#
# "0.5".to_d # => 0.5e0
# "123.45e1".to_d # => 0.12345e4
# "45.67 degrees".to_d # => 0.4567e2
#
# See also BigDecimal::new.
#
def to_d
BigDecimal.interpret_loosely(self)
end
end
class BigDecimal < Numeric
# call-seq:
# a.to_digits -> string
#
# Converts a BigDecimal to a String of the form "nnnnnn.mmm".
# This method is deprecated; use BigDecimal#to_s("F") instead.
#
# require 'bigdecimal/util'
#
# d = BigDecimal("3.14")
# d.to_digits # => "3.14"
#
def to_digits
if self.nan? || self.infinite? || self.zero?
self.to_s
else
i = self.to_i.to_s
_,f,_,z = self.frac.split
i + "." + ("0"*(-z)) + f
end
end
# call-seq:
# a.to_d -> bigdecimal
#
# Returns self.
#
# require 'bigdecimal/util'
#
# d = BigDecimal("3.14")
# d.to_d # => 0.314e1
#
def to_d
self
end
end
class Rational < Numeric
# call-seq:
# rat.to_d(precision) -> bigdecimal
#
# Returns the value as a BigDecimal.
#
# The required +precision+ parameter is used to determine the number of
# significant digits for the result.
#
# require 'bigdecimal'
# require 'bigdecimal/util'
#
# Rational(22, 7).to_d(3) # => 0.314e1
#
# See also BigDecimal::new.
#
def to_d(precision)
BigDecimal(self, precision)
end
end
class Complex < Numeric
# call-seq:
# cmp.to_d -> bigdecimal
# cmp.to_d(precision) -> bigdecimal
#
# Returns the value as a BigDecimal.
#
# The +precision+ parameter is required for a rational complex number.
# This parameter is used to determine the number of significant digits
# for the result.
#
# require 'bigdecimal'
# require 'bigdecimal/util'
#
# Complex(0.1234567, 0).to_d(4) # => 0.1235e0
# Complex(Rational(22, 7), 0).to_d(3) # => 0.314e1
#
# See also BigDecimal::new.
#
def to_d(*args)
BigDecimal(self) unless self.imag.zero? # to raise eerror
if args.length == 0
case self.real
when Rational
BigDecimal(self.real) # to raise error
end
end
self.real.to_d(*args)
end
end
class NilClass
# call-seq:
# nil.to_d -> bigdecimal
#
# Returns nil represented as a BigDecimal.
#
# require 'bigdecimal'
# require 'bigdecimal/util'
#
# nil.to_d # => 0.0
#
def to_d
BigDecimal(0)
end
end

View File

@@ -1,662 +0,0 @@
# frozen_string_literal: true
require_relative "bundler/vendored_fileutils"
require "pathname"
require "rbconfig"
require_relative "bundler/errors"
require_relative "bundler/environment_preserver"
require_relative "bundler/plugin"
require_relative "bundler/rubygems_ext"
require_relative "bundler/rubygems_integration"
require_relative "bundler/version"
require_relative "bundler/constants"
require_relative "bundler/current_ruby"
require_relative "bundler/build_metadata"
# Bundler provides a consistent environment for Ruby projects by
# tracking and installing the exact gems and versions that are needed.
#
# Since Ruby 2.6, Bundler is a part of Ruby's standard library.
#
# Bundler is used by creating _gemfiles_ listing all the project dependencies
# and (optionally) their versions and then using
#
# require 'bundler/setup'
#
# or Bundler.setup to setup environment where only specified gems and their
# specified versions could be used.
#
# See {Bundler website}[https://bundler.io/docs.html] for extensive documentation
# on gemfiles creation and Bundler usage.
#
# As a standard library inside project, Bundler could be used for introspection
# of loaded and required modules.
#
module Bundler
environment_preserver = EnvironmentPreserver.from_env
ORIGINAL_ENV = environment_preserver.restore
environment_preserver.replace_with_backup
SUDO_MUTEX = Thread::Mutex.new
autoload :Definition, File.expand_path("bundler/definition", __dir__)
autoload :Dependency, File.expand_path("bundler/dependency", __dir__)
autoload :Deprecate, File.expand_path("bundler/deprecate", __dir__)
autoload :Digest, File.expand_path("bundler/digest", __dir__)
autoload :Dsl, File.expand_path("bundler/dsl", __dir__)
autoload :EndpointSpecification, File.expand_path("bundler/endpoint_specification", __dir__)
autoload :Env, File.expand_path("bundler/env", __dir__)
autoload :Fetcher, File.expand_path("bundler/fetcher", __dir__)
autoload :FeatureFlag, File.expand_path("bundler/feature_flag", __dir__)
autoload :GemHelper, File.expand_path("bundler/gem_helper", __dir__)
autoload :GemHelpers, File.expand_path("bundler/gem_helpers", __dir__)
autoload :GemVersionPromoter, File.expand_path("bundler/gem_version_promoter", __dir__)
autoload :Graph, File.expand_path("bundler/graph", __dir__)
autoload :Index, File.expand_path("bundler/index", __dir__)
autoload :Injector, File.expand_path("bundler/injector", __dir__)
autoload :Installer, File.expand_path("bundler/installer", __dir__)
autoload :LazySpecification, File.expand_path("bundler/lazy_specification", __dir__)
autoload :LockfileParser, File.expand_path("bundler/lockfile_parser", __dir__)
autoload :MatchRemoteMetadata, File.expand_path("bundler/match_remote_metadata", __dir__)
autoload :ProcessLock, File.expand_path("bundler/process_lock", __dir__)
autoload :RemoteSpecification, File.expand_path("bundler/remote_specification", __dir__)
autoload :Resolver, File.expand_path("bundler/resolver", __dir__)
autoload :Retry, File.expand_path("bundler/retry", __dir__)
autoload :RubyDsl, File.expand_path("bundler/ruby_dsl", __dir__)
autoload :RubyVersion, File.expand_path("bundler/ruby_version", __dir__)
autoload :Runtime, File.expand_path("bundler/runtime", __dir__)
autoload :SelfManager, File.expand_path("bundler/self_manager", __dir__)
autoload :Settings, File.expand_path("bundler/settings", __dir__)
autoload :SharedHelpers, File.expand_path("bundler/shared_helpers", __dir__)
autoload :Source, File.expand_path("bundler/source", __dir__)
autoload :SourceList, File.expand_path("bundler/source_list", __dir__)
autoload :SourceMap, File.expand_path("bundler/source_map", __dir__)
autoload :SpecSet, File.expand_path("bundler/spec_set", __dir__)
autoload :StubSpecification, File.expand_path("bundler/stub_specification", __dir__)
autoload :UI, File.expand_path("bundler/ui", __dir__)
autoload :URICredentialsFilter, File.expand_path("bundler/uri_credentials_filter", __dir__)
autoload :URINormalizer, File.expand_path("bundler/uri_normalizer", __dir__)
autoload :SafeMarshal, File.expand_path("bundler/safe_marshal", __dir__)
class << self
def configure
@configure ||= configure_gem_home_and_path
end
def ui
(defined?(@ui) && @ui) || (self.ui = UI::Shell.new)
end
def ui=(ui)
Bundler.rubygems.ui = UI::RGProxy.new(ui)
@ui = ui
end
# Returns absolute path of where gems are installed on the filesystem.
def bundle_path
@bundle_path ||= Pathname.new(configured_bundle_path.path).expand_path(root)
end
def create_bundle_path
SharedHelpers.filesystem_access(bundle_path.to_s) do |p|
mkdir_p(p)
end unless bundle_path.exist?
@bundle_path = bundle_path.realpath
rescue Errno::EEXIST
raise PathError, "Could not install to path `#{bundle_path}` " \
"because a file already exists at that path. Either remove or rename the file so the directory can be created."
end
def configured_bundle_path
@configured_bundle_path ||= settings.path.tap(&:validate!)
end
# Returns absolute location of where binstubs are installed to.
def bin_path
@bin_path ||= begin
path = settings[:bin] || "bin"
path = Pathname.new(path).expand_path(root).expand_path
SharedHelpers.filesystem_access(path) {|p| FileUtils.mkdir_p(p) }
path
end
end
# Turns on the Bundler runtime. After +Bundler.setup+ call, all +load+ or
# +require+ of the gems would be allowed only if they are part of
# the Gemfile or Ruby's standard library. If the versions specified
# in Gemfile, only those versions would be loaded.
#
# Assuming Gemfile
#
# gem 'first_gem', '= 1.0'
# group :test do
# gem 'second_gem', '= 1.0'
# end
#
# The code using Bundler.setup works as follows:
#
# require 'third_gem' # allowed, required from global gems
# require 'first_gem' # allowed, loads the last installed version
# Bundler.setup
# require 'fourth_gem' # fails with LoadError
# require 'second_gem' # loads exactly version 1.0
#
# +Bundler.setup+ can be called only once, all subsequent calls are no-op.
#
# If _groups_ list is provided, only gems from specified groups would
# be allowed (gems specified outside groups belong to special +:default+ group).
#
# To require all gems from Gemfile (or only some groups), see Bundler.require.
#
def setup(*groups)
# Return if all groups are already loaded
return @setup if defined?(@setup) && @setup
definition.validate_runtime!
SharedHelpers.print_major_deprecations!
if groups.empty?
# Load all groups, but only once
@setup = load.setup
else
load.setup(*groups)
end
end
# Setups Bundler environment (see Bundler.setup) if it is not already set,
# and loads all gems from groups specified. Unlike ::setup, can be called
# multiple times with different groups (if they were allowed by setup).
#
# Assuming Gemfile
#
# gem 'first_gem', '= 1.0'
# group :test do
# gem 'second_gem', '= 1.0'
# end
#
# The code will work as follows:
#
# Bundler.setup # allow all groups
# Bundler.require(:default) # requires only first_gem
# # ...later
# Bundler.require(:test) # requires second_gem
#
def require(*groups)
setup(*groups).require(*groups)
end
def load
@load ||= Runtime.new(root, definition)
end
def environment
SharedHelpers.major_deprecation 2, "Bundler.environment has been removed in favor of Bundler.load", :print_caller_location => true
load
end
# Returns an instance of Bundler::Definition for given Gemfile and lockfile
#
# @param unlock [Hash, Boolean, nil] Gems that have been requested
# to be updated or true if all gems should be updated
# @return [Bundler::Definition]
def definition(unlock = nil)
@definition = nil if unlock
@definition ||= begin
configure
Definition.build(default_gemfile, default_lockfile, unlock)
end
end
def frozen_bundle?
frozen = settings[:frozen]
return frozen unless frozen.nil?
settings[:deployment]
end
def locked_gems
@locked_gems ||=
if defined?(@definition) && @definition
definition.locked_gems
elsif Bundler.default_lockfile.file?
lock = Bundler.read_file(Bundler.default_lockfile)
LockfileParser.new(lock)
end
end
def most_specific_locked_platform?(platform)
return false unless defined?(@definition) && @definition
definition.most_specific_locked_platform == platform
end
def ruby_scope
"#{Bundler.rubygems.ruby_engine}/#{RbConfig::CONFIG["ruby_version"]}"
end
def user_home
@user_home ||= begin
home = Bundler.rubygems.user_home
bundle_home = home ? File.join(home, ".bundle") : nil
warning = if home.nil?
"Your home directory is not set."
elsif !File.directory?(home)
"`#{home}` is not a directory."
elsif !File.writable?(home) && (!File.directory?(bundle_home) || !File.writable?(bundle_home))
"`#{home}` is not writable."
end
if warning
Bundler.ui.warn "#{warning}\n"
user_home = tmp_home_path
Bundler.ui.warn "Bundler will use `#{user_home}' as your home directory temporarily.\n"
user_home
else
Pathname.new(home)
end
end
end
def user_bundle_path(dir = "home")
env_var, fallback = case dir
when "home"
["BUNDLE_USER_HOME", proc { Pathname.new(user_home).join(".bundle") }]
when "cache"
["BUNDLE_USER_CACHE", proc { user_bundle_path.join("cache") }]
when "config"
["BUNDLE_USER_CONFIG", proc { user_bundle_path.join("config") }]
when "plugin"
["BUNDLE_USER_PLUGIN", proc { user_bundle_path.join("plugin") }]
else
raise BundlerError, "Unknown user path requested: #{dir}"
end
# `fallback` will already be a Pathname, but Pathname.new() is
# idempotent so it's OK
Pathname.new(ENV.fetch(env_var, &fallback))
end
def user_cache
user_bundle_path("cache")
end
def home
bundle_path.join("bundler")
end
def install_path
home.join("gems")
end
def specs_path
bundle_path.join("specifications")
end
def root
@root ||= begin
SharedHelpers.root
rescue GemfileNotFound
bundle_dir = default_bundle_dir
raise GemfileNotFound, "Could not locate Gemfile or .bundle/ directory" unless bundle_dir
Pathname.new(File.expand_path("..", bundle_dir))
end
end
def app_config_path
if app_config = ENV["BUNDLE_APP_CONFIG"]
app_config_pathname = Pathname.new(app_config)
if app_config_pathname.absolute?
app_config_pathname
else
app_config_pathname.expand_path(root)
end
else
root.join(".bundle")
end
end
def app_cache(custom_path = nil)
path = custom_path || root
Pathname.new(path).join(settings.app_cache_path)
end
def tmp(name = Process.pid.to_s)
Kernel.send(:require, "tmpdir")
Pathname.new(Dir.mktmpdir(["bundler", name]))
end
def rm_rf(path)
FileUtils.remove_entry_secure(path) if path && File.exist?(path)
rescue ArgumentError
message = <<EOF
It is a security vulnerability to allow your home directory to be world-writable, and bundler cannot continue.
You should probably consider fixing this issue by running `chmod o-w ~` on *nix.
Please refer to https://ruby-doc.org/stdlib-3.1.2/libdoc/fileutils/rdoc/FileUtils.html#method-c-remove_entry_secure for details.
EOF
File.world_writable?(path) ? Bundler.ui.warn(message) : raise
raise PathError, "Please fix the world-writable issue with your #{path} directory"
end
def settings
@settings ||= Settings.new(app_config_path)
rescue GemfileNotFound
@settings = Settings.new(Pathname.new(".bundle").expand_path)
end
# @return [Hash] Environment present before Bundler was activated
def original_env
ORIGINAL_ENV.clone
end
# @deprecated Use `unbundled_env` instead
def clean_env
Bundler::SharedHelpers.major_deprecation(
2,
"`Bundler.clean_env` has been deprecated in favor of `Bundler.unbundled_env`. " \
"If you instead want the environment before bundler was originally loaded, use `Bundler.original_env`",
:print_caller_location => true
)
unbundled_env
end
# @return [Hash] Environment with all bundler-related variables removed
def unbundled_env
env = original_env
if env.key?("BUNDLER_ORIG_MANPATH")
env["MANPATH"] = env["BUNDLER_ORIG_MANPATH"]
end
env.delete_if {|k, _| k[0, 7] == "BUNDLE_" }
if env.key?("RUBYOPT")
rubyopt = env["RUBYOPT"].split(" ")
rubyopt.delete("-r#{File.expand_path("bundler/setup", __dir__)}")
rubyopt.delete("-rbundler/setup")
env["RUBYOPT"] = rubyopt.join(" ")
end
if env.key?("RUBYLIB")
rubylib = env["RUBYLIB"].split(File::PATH_SEPARATOR)
rubylib.delete(__dir__)
env["RUBYLIB"] = rubylib.join(File::PATH_SEPARATOR)
end
env
end
# Run block with environment present before Bundler was activated
def with_original_env
with_env(original_env) { yield }
end
# @deprecated Use `with_unbundled_env` instead
def with_clean_env
Bundler::SharedHelpers.major_deprecation(
2,
"`Bundler.with_clean_env` has been deprecated in favor of `Bundler.with_unbundled_env`. " \
"If you instead want the environment before bundler was originally loaded, use `Bundler.with_original_env`",
:print_caller_location => true
)
with_env(unbundled_env) { yield }
end
# Run block with all bundler-related variables removed
def with_unbundled_env
with_env(unbundled_env) { yield }
end
# Run subcommand with the environment present before Bundler was activated
def original_system(*args)
with_original_env { Kernel.system(*args) }
end
# @deprecated Use `unbundled_system` instead
def clean_system(*args)
Bundler::SharedHelpers.major_deprecation(
2,
"`Bundler.clean_system` has been deprecated in favor of `Bundler.unbundled_system`. " \
"If you instead want to run the command in the environment before bundler was originally loaded, use `Bundler.original_system`",
:print_caller_location => true
)
with_env(unbundled_env) { Kernel.system(*args) }
end
# Run subcommand in an environment with all bundler related variables removed
def unbundled_system(*args)
with_unbundled_env { Kernel.system(*args) }
end
# Run a `Kernel.exec` to a subcommand with the environment present before Bundler was activated
def original_exec(*args)
with_original_env { Kernel.exec(*args) }
end
# @deprecated Use `unbundled_exec` instead
def clean_exec(*args)
Bundler::SharedHelpers.major_deprecation(
2,
"`Bundler.clean_exec` has been deprecated in favor of `Bundler.unbundled_exec`. " \
"If you instead want to exec to a command in the environment before bundler was originally loaded, use `Bundler.original_exec`",
:print_caller_location => true
)
with_env(unbundled_env) { Kernel.exec(*args) }
end
# Run a `Kernel.exec` to a subcommand in an environment with all bundler related variables removed
def unbundled_exec(*args)
with_env(unbundled_env) { Kernel.exec(*args) }
end
def local_platform
return Gem::Platform::RUBY if settings[:force_ruby_platform]
Gem::Platform.local
end
def default_gemfile
SharedHelpers.default_gemfile
end
def default_lockfile
SharedHelpers.default_lockfile
end
def default_bundle_dir
SharedHelpers.default_bundle_dir
end
def system_bindir
# Gem.bindir doesn't always return the location that RubyGems will install
# system binaries. If you put '-n foo' in your .gemrc, RubyGems will
# install binstubs there instead. Unfortunately, RubyGems doesn't expose
# that directory at all, so rather than parse .gemrc ourselves, we allow
# the directory to be set as well, via `bundle config set --local bindir foo`.
Bundler.settings[:system_bindir] || Bundler.rubygems.gem_bindir
end
def preferred_gemfile_name
Bundler.settings[:init_gems_rb] ? "gems.rb" : "Gemfile"
end
def use_system_gems?
configured_bundle_path.use_system_gems?
end
def mkdir_p(path, options = {})
SharedHelpers.filesystem_access(path, :write) do |p|
FileUtils.mkdir_p(p)
end
end
def which(executable)
if File.file?(executable) && File.executable?(executable)
executable
elsif paths = ENV["PATH"]
quote = '"'
paths.split(File::PATH_SEPARATOR).find do |path|
path = path[1..-2] if path.start_with?(quote) && path.end_with?(quote)
executable_path = File.expand_path(executable, path)
return executable_path if File.file?(executable_path) && File.executable?(executable_path)
end
end
end
def read_file(file)
SharedHelpers.filesystem_access(file, :read) do
File.open(file, "r:UTF-8", &:read)
end
end
def safe_load_marshal(data)
load_marshal(data, :marshal_proc => SafeMarshal.proc)
end
def load_gemspec(file, validate = false)
@gemspec_cache ||= {}
key = File.expand_path(file)
@gemspec_cache[key] ||= load_gemspec_uncached(file, validate)
# Protect against caching side-effected gemspecs by returning a
# new instance each time.
@gemspec_cache[key]&.dup
end
def load_gemspec_uncached(file, validate = false)
path = Pathname.new(file)
contents = read_file(file)
spec = if contents.start_with?("---") # YAML header
eval_yaml_gemspec(path, contents)
else
# Eval the gemspec from its parent directory, because some gemspecs
# depend on "./" relative paths.
SharedHelpers.chdir(path.dirname.to_s) do
eval_gemspec(path, contents)
end
end
return unless spec
spec.loaded_from = path.expand_path.to_s
Bundler.rubygems.validate(spec) if validate
spec
end
def clear_gemspec_cache
@gemspec_cache = {}
end
def git_present?
return @git_present if defined?(@git_present)
@git_present = Bundler.which("git#{RbConfig::CONFIG["EXEEXT"]}")
end
def feature_flag
@feature_flag ||= FeatureFlag.new(VERSION)
end
def reset!
reset_paths!
Plugin.reset!
reset_rubygems!
end
def reset_settings_and_root!
@settings = nil
@root = nil
end
def reset_paths!
@bin_path = nil
@bundler_major_version = nil
@bundle_path = nil
@configure = nil
@configured_bundle_path = nil
@definition = nil
@load = nil
@locked_gems = nil
@root = nil
@settings = nil
@setup = nil
@user_home = nil
end
def reset_rubygems!
return unless defined?(@rubygems) && @rubygems
rubygems.undo_replacements
rubygems.reset
@rubygems = nil
end
def configure_gem_home_and_path(path = bundle_path)
configure_gem_path
configure_gem_home(path)
Bundler.rubygems.clear_paths
end
def self_manager
@self_manager ||= begin
require_relative "bundler/self_manager"
Bundler::SelfManager.new
end
end
private
def load_marshal(data, marshal_proc: nil)
Marshal.load(data, marshal_proc)
rescue TypeError => e
raise MarshalError, "#{e.class}: #{e.message}"
end
def eval_yaml_gemspec(path, contents)
Kernel.require "psych"
Gem::Specification.from_yaml(contents)
rescue ::Psych::SyntaxError, ArgumentError, Gem::EndOfYAMLException, Gem::Exception
eval_gemspec(path, contents)
end
def eval_gemspec(path, contents)
eval(contents, TOPLEVEL_BINDING.dup, path.expand_path.to_s)
rescue ScriptError, StandardError => e
msg = "There was an error while loading `#{path.basename}`: #{e.message}"
raise GemspecError, Dsl::DSLError.new(msg, path, e.backtrace, contents)
end
def configure_gem_path
unless use_system_gems?
# this needs to be empty string to cause
# PathSupport.split_gem_path to only load up the
# Bundler --path setting as the GEM_PATH.
Bundler::SharedHelpers.set_env "GEM_PATH", ""
end
end
def configure_gem_home(path)
Bundler::SharedHelpers.set_env "GEM_HOME", path.to_s
end
def tmp_home_path
Kernel.send(:require, "tmpdir")
SharedHelpers.filesystem_access(Dir.tmpdir) do
path = Bundler.tmp
at_exit { Bundler.rm_rf(path) }
path
end
end
# @param env [Hash]
def with_env(env)
backup = ENV.to_hash
ENV.replace(env)
yield
ensure
ENV.replace(backup)
end
end
end

View File

@@ -1,43 +0,0 @@
# frozen_string_literal: true
module Bundler
# Represents metadata from when the Bundler gem was built.
module BuildMetadata
# begin ivars
@release = false
# end ivars
# A hash representation of the build metadata.
def self.to_h
{
"Built At" => built_at,
"Git SHA" => git_commit_sha,
"Released Version" => release?,
}
end
# A string representing the date the bundler gem was built.
def self.built_at
@built_at ||= Time.now.utc.strftime("%Y-%m-%d").freeze
end
# The SHA for the git commit the bundler gem was built from.
def self.git_commit_sha
return @git_commit_sha if instance_variable_defined? :@git_commit_sha
# If Bundler has been installed without its .git directory and without a
# commit instance variable then we can't determine its commits SHA.
git_dir = File.expand_path("../../../.git", __dir__)
if File.directory?(git_dir)
return @git_commit_sha = Dir.chdir(git_dir) { `git rev-parse --short HEAD`.strip.freeze }
end
@git_commit_sha ||= "unknown"
end
# Whether this is an official release build of Bundler.
def self.release?
@release
end
end
end

View File

@@ -1,22 +0,0 @@
# frozen_string_literal: true
require_relative "shared_helpers"
Bundler::SharedHelpers.major_deprecation 2,
"The Bundler task for Capistrano. Please use https://github.com/capistrano/bundler"
# Capistrano task for Bundler.
#
# Add "require 'bundler/capistrano'" in your Capistrano deploy.rb, and
# Bundler will be activated after each new deployment.
require_relative "deployment"
require "capistrano/version"
if defined?(Capistrano::Version) && Gem::Version.new(Capistrano::Version).release >= Gem::Version.new("3.0")
raise "For Capistrano 3.x integration, please use https://github.com/capistrano/bundler"
end
Capistrano::Configuration.instance(:must_exist).load do
before "deploy:finalize_update", "bundle:install"
Bundler::Deployment.define_task(self, :task, :except => { :no_release => true })
set :rake, lambda { "#{fetch(:bundle_cmd, "bundle")} exec rake" }
end

View File

@@ -1,894 +0,0 @@
# frozen_string_literal: true
require_relative "vendored_thor"
module Bundler
class CLI < Thor
require_relative "cli/common"
package_name "Bundler"
AUTO_INSTALL_CMDS = %w[show binstubs outdated exec open console licenses clean].freeze
PARSEABLE_COMMANDS = %w[check config help exec platform show version].freeze
EXTENSIONS = ["c", "rust"].freeze
COMMAND_ALIASES = {
"check" => "c",
"install" => "i",
"plugin" => "",
"list" => "ls",
"exec" => ["e", "ex", "exe"],
"cache" => ["package", "pack"],
"version" => ["-v", "--version"],
}.freeze
def self.start(*)
check_deprecated_ext_option(ARGV) if ARGV.include?("--ext")
super
ensure
Bundler::SharedHelpers.print_major_deprecations!
end
def self.dispatch(*)
super do |i|
i.send(:print_command)
i.send(:warn_on_outdated_bundler)
end
end
def self.all_aliases
@all_aliases ||= begin
command_aliases = {}
COMMAND_ALIASES.each do |name, aliases|
Array(aliases).each do |one_alias|
command_aliases[one_alias] = name
end
end
command_aliases
end
end
def self.aliases_for(command_name)
COMMAND_ALIASES.select {|k, _| k == command_name }.invert
end
def initialize(*args)
super
custom_gemfile = options[:gemfile] || Bundler.settings[:gemfile]
if custom_gemfile && !custom_gemfile.empty?
Bundler::SharedHelpers.set_env "BUNDLE_GEMFILE", File.expand_path(custom_gemfile)
Bundler.reset_settings_and_root!
end
Bundler.self_manager.restart_with_locked_bundler_if_needed
Bundler.settings.set_command_option_if_given :retry, options[:retry]
current_cmd = args.last[:current_command].name
auto_install if AUTO_INSTALL_CMDS.include?(current_cmd)
rescue UnknownArgumentError => e
raise InvalidOption, e.message
ensure
self.options ||= {}
unprinted_warnings = Bundler.ui.unprinted_warnings
Bundler.ui = UI::Shell.new(options)
Bundler.ui.level = "debug" if options["verbose"]
unprinted_warnings.each {|w| Bundler.ui.warn(w) }
end
check_unknown_options!(:except => [:config, :exec])
stop_on_unknown_option! :exec
desc "cli_help", "Prints a summary of bundler commands", :hide => true
def cli_help
version
Bundler.ui.info "\n"
primary_commands = ["install", "update", "cache", "exec", "config", "help"]
list = self.class.printable_commands(true)
by_name = list.group_by {|name, _message| name.match(/^bundle (\w+)/)[1] }
utilities = by_name.keys.sort - primary_commands
primary_commands.map! {|name| (by_name[name] || raise("no primary command #{name}")).first }
utilities.map! {|name| by_name[name].first }
shell.say "Bundler commands:\n\n"
shell.say " Primary commands:\n"
shell.print_table(primary_commands, :indent => 4, :truncate => true)
shell.say
shell.say " Utilities:\n"
shell.print_table(utilities, :indent => 4, :truncate => true)
shell.say
self.class.send(:class_options_help, shell)
end
default_task(Bundler.feature_flag.default_cli_command)
class_option "no-color", :type => :boolean, :desc => "Disable colorization in output"
class_option "retry", :type => :numeric, :aliases => "-r", :banner => "NUM",
:desc => "Specify the number of times you wish to attempt network commands"
class_option "verbose", :type => :boolean, :desc => "Enable verbose output mode", :aliases => "-V"
def help(cli = nil)
case cli
when "gemfile" then command = "gemfile"
when nil then command = "bundle"
else command = "bundle-#{cli}"
end
man_path = File.expand_path("man", __dir__)
man_pages = Hash[Dir.glob(File.join(man_path, "**", "*")).grep(/.*\.\d*\Z/).collect do |f|
[File.basename(f, ".*"), f]
end]
if man_pages.include?(command)
man_page = man_pages[command]
if Bundler.which("man") && man_path !~ %r{^file:/.+!/META-INF/jruby.home/.+}
Kernel.exec "man #{man_page}"
else
puts File.read("#{man_path}/#{File.basename(man_page)}.ronn")
end
elsif command_path = Bundler.which("bundler-#{cli}")
Kernel.exec(command_path, "--help")
else
super
end
end
def self.handle_no_command_error(command, has_namespace = $thor_runner)
if Bundler.feature_flag.plugins? && Bundler::Plugin.command?(command)
return Bundler::Plugin.exec_command(command, ARGV[1..-1])
end
return super unless command_path = Bundler.which("bundler-#{command}")
Kernel.exec(command_path, *ARGV[1..-1])
end
desc "init [OPTIONS]", "Generates a Gemfile into the current working directory"
long_desc <<-D
Init generates a default Gemfile in the current working directory. When adding a
Gemfile to a gem with a gemspec, the --gemspec option will automatically add each
dependency listed in the gemspec file to the newly created Gemfile.
D
method_option "gemspec", :type => :string, :banner => "Use the specified .gemspec to create the Gemfile"
method_option "gemfile", :type => :string, :banner => "Use the specified name for the gemfile instead of 'Gemfile'"
def init
require_relative "cli/init"
Init.new(options.dup).run
end
desc "check [OPTIONS]", "Checks if the dependencies listed in Gemfile are satisfied by currently installed gems"
long_desc <<-D
Check searches the local machine for each of the gems requested in the Gemfile. If
all gems are found, Bundler prints a success message and exits with a status of 0.
If not, the first missing gem is listed and Bundler exits status 1.
D
method_option "dry-run", :type => :boolean, :default => false, :banner =>
"Lock the Gemfile"
method_option "gemfile", :type => :string, :banner =>
"Use the specified gemfile instead of Gemfile"
method_option "path", :type => :string, :banner =>
"Specify a different path than the system default ($BUNDLE_PATH or $GEM_HOME).#{" Bundler will remember this value for future installs on this machine" unless Bundler.feature_flag.forget_cli_options?}"
def check
remembered_flag_deprecation("path")
require_relative "cli/check"
Check.new(options).run
end
map aliases_for("check")
desc "remove [GEM [GEM ...]]", "Removes gems from the Gemfile"
long_desc <<-D
Removes the given gems from the Gemfile while ensuring that the resulting Gemfile is still valid. If the gem is not found, Bundler prints a error message and if gem could not be removed due to any reason Bundler will display a warning.
D
method_option "install", :type => :boolean, :banner =>
"Runs 'bundle install' after removing the gems from the Gemfile"
def remove(*gems)
SharedHelpers.major_deprecation(2, "The `--install` flag has been deprecated. `bundle install` is triggered by default.") if ARGV.include?("--install")
require_relative "cli/remove"
Remove.new(gems, options).run
end
desc "install [OPTIONS]", "Install the current environment to the system"
long_desc <<-D
Install will install all of the gems in the current bundle, making them available
for use. In a freshly checked out repository, this command will give you the same
gem versions as the last person who updated the Gemfile and ran `bundle update`.
Passing [DIR] to install (e.g. vendor) will cause the unpacked gems to be installed
into the [DIR] directory rather than into system gems.
If the bundle has already been installed, bundler will tell you so and then exit.
D
method_option "binstubs", :type => :string, :lazy_default => "bin", :banner =>
"Generate bin stubs for bundled gems to ./bin"
method_option "clean", :type => :boolean, :banner =>
"Run bundle clean automatically after install"
method_option "deployment", :type => :boolean, :banner =>
"Install using defaults tuned for deployment environments"
method_option "frozen", :type => :boolean, :banner =>
"Do not allow the Gemfile.lock to be updated after this install"
method_option "full-index", :type => :boolean, :banner =>
"Fall back to using the single-file index of all gems"
method_option "gemfile", :type => :string, :banner =>
"Use the specified gemfile instead of Gemfile"
method_option "jobs", :aliases => "-j", :type => :numeric, :banner =>
"Specify the number of jobs to run in parallel"
method_option "local", :type => :boolean, :banner =>
"Do not attempt to fetch gems remotely and use the gem cache instead"
method_option "prefer-local", :type => :boolean, :banner =>
"Only attempt to fetch gems remotely if not present locally, even if newer versions are available remotely"
method_option "no-cache", :type => :boolean, :banner =>
"Don't update the existing gem cache."
method_option "redownload", :type => :boolean, :aliases => "--force", :banner =>
"Force downloading every gem."
method_option "no-prune", :type => :boolean, :banner =>
"Don't remove stale gems from the cache."
method_option "path", :type => :string, :banner =>
"Specify a different path than the system default ($BUNDLE_PATH or $GEM_HOME).#{" Bundler will remember this value for future installs on this machine" unless Bundler.feature_flag.forget_cli_options?}"
method_option "quiet", :type => :boolean, :banner =>
"Only output warnings and errors."
method_option "shebang", :type => :string, :banner =>
"Specify a different shebang executable name than the default (usually 'ruby')"
method_option "standalone", :type => :array, :lazy_default => [], :banner =>
"Make a bundle that can work without the Bundler runtime"
method_option "system", :type => :boolean, :banner =>
"Install to the system location ($BUNDLE_PATH or $GEM_HOME) even if the bundle was previously installed somewhere else for this application"
method_option "trust-policy", :alias => "P", :type => :string, :banner =>
"Gem trust policy (like gem install -P). Must be one of " +
Bundler.rubygems.security_policy_keys.join("|")
method_option "without", :type => :array, :banner =>
"Exclude gems that are part of the specified named group."
method_option "with", :type => :array, :banner =>
"Include gems that are part of the specified named group."
def install
SharedHelpers.major_deprecation(2, "The `--force` option has been renamed to `--redownload`") if ARGV.include?("--force")
%w[clean deployment frozen no-prune path shebang system without with].each do |option|
remembered_flag_deprecation(option)
end
remembered_negative_flag_deprecation("no-deployment")
require_relative "cli/install"
Bundler.settings.temporary(:no_install => false) do
Install.new(options.dup).run
end
end
map aliases_for("install")
desc "update [OPTIONS]", "Update the current environment"
long_desc <<-D
Update will install the newest versions of the gems listed in the Gemfile. Use
update when you have changed the Gemfile, or if you want to get the newest
possible versions of the gems in the bundle.
D
method_option "full-index", :type => :boolean, :banner =>
"Fall back to using the single-file index of all gems"
method_option "gemfile", :type => :string, :banner =>
"Use the specified gemfile instead of Gemfile"
method_option "group", :aliases => "-g", :type => :array, :banner =>
"Update a specific group"
method_option "jobs", :aliases => "-j", :type => :numeric, :banner =>
"Specify the number of jobs to run in parallel"
method_option "local", :type => :boolean, :banner =>
"Do not attempt to fetch gems remotely and use the gem cache instead"
method_option "quiet", :type => :boolean, :banner =>
"Only output warnings and errors."
method_option "source", :type => :array, :banner =>
"Update a specific source (and all gems associated with it)"
method_option "redownload", :type => :boolean, :aliases => "--force", :banner =>
"Force downloading every gem."
method_option "ruby", :type => :boolean, :banner =>
"Update ruby specified in Gemfile.lock"
method_option "bundler", :type => :string, :lazy_default => "> 0.a", :banner =>
"Update the locked version of bundler"
method_option "patch", :type => :boolean, :banner =>
"Prefer updating only to next patch version"
method_option "minor", :type => :boolean, :banner =>
"Prefer updating only to next minor version"
method_option "major", :type => :boolean, :banner =>
"Prefer updating to next major version (default)"
method_option "pre", :type => :boolean, :banner =>
"Always choose the highest allowed version when updating gems, regardless of prerelease status"
method_option "strict", :type => :boolean, :banner =>
"Do not allow any gem to be updated past latest --patch | --minor | --major"
method_option "conservative", :type => :boolean, :banner =>
"Use bundle install conservative update behavior and do not allow shared dependencies to be updated."
method_option "all", :type => :boolean, :banner =>
"Update everything."
def update(*gems)
SharedHelpers.major_deprecation(2, "The `--force` option has been renamed to `--redownload`") if ARGV.include?("--force")
require_relative "cli/update"
Bundler.settings.temporary(:no_install => false) do
Update.new(options, gems).run
end
end
desc "show GEM [OPTIONS]", "Shows all gems that are part of the bundle, or the path to a given gem"
long_desc <<-D
Show lists the names and versions of all gems that are required by your Gemfile.
Calling show with [GEM] will list the exact location of that gem on your machine.
D
method_option "paths", :type => :boolean,
:banner => "List the paths of all gems that are required by your Gemfile."
method_option "outdated", :type => :boolean,
:banner => "Show verbose output including whether gems are outdated."
def show(gem_name = nil)
SharedHelpers.major_deprecation(2, "the `--outdated` flag to `bundle show` was undocumented and will be removed without replacement") if ARGV.include?("--outdated")
require_relative "cli/show"
Show.new(options, gem_name).run
end
desc "list", "List all gems in the bundle"
method_option "name-only", :type => :boolean, :banner => "print only the gem names"
method_option "only-group", :type => :array, :default => [], :banner => "print gems from a given set of groups"
method_option "without-group", :type => :array, :default => [], :banner => "print all gems except from a given set of groups"
method_option "paths", :type => :boolean, :banner => "print the path to each gem in the bundle"
def list
require_relative "cli/list"
List.new(options).run
end
map aliases_for("list")
desc "info GEM [OPTIONS]", "Show information for the given gem"
method_option "path", :type => :boolean, :banner => "Print full path to gem"
method_option "version", :type => :boolean, :banner => "Print gem version"
def info(gem_name)
require_relative "cli/info"
Info.new(options, gem_name).run
end
desc "binstubs GEM [OPTIONS]", "Install the binstubs of the listed gem"
long_desc <<-D
Generate binstubs for executables in [GEM]. Binstubs are put into bin,
or the --binstubs directory if one has been set. Calling binstubs with [GEM [GEM]]
will create binstubs for all given gems.
D
method_option "force", :type => :boolean, :default => false, :banner =>
"Overwrite existing binstubs if they exist"
method_option "path", :type => :string, :lazy_default => "bin", :banner =>
"Binstub destination directory (default bin)"
method_option "shebang", :type => :string, :banner =>
"Specify a different shebang executable name than the default (usually 'ruby')"
method_option "standalone", :type => :boolean, :banner =>
"Make binstubs that can work without the Bundler runtime"
method_option "all", :type => :boolean, :banner =>
"Install binstubs for all gems"
method_option "all-platforms", :type => :boolean, :default => false, :banner =>
"Install binstubs for all platforms"
def binstubs(*gems)
require_relative "cli/binstubs"
Binstubs.new(options, gems).run
end
desc "add GEM VERSION", "Add gem to Gemfile and run bundle install"
long_desc <<-D
Adds the specified gem to Gemfile (if valid) and run 'bundle install' in one step.
D
method_option "version", :aliases => "-v", :type => :string
method_option "group", :aliases => "-g", :type => :string
method_option "source", :aliases => "-s", :type => :string
method_option "require", :aliases => "-r", :type => :string, :banner => "Adds require path to gem. Provide false, or a path as a string."
method_option "path", :type => :string
method_option "git", :type => :string
method_option "github", :type => :string
method_option "branch", :type => :string
method_option "ref", :type => :string
method_option "skip-install", :type => :boolean, :banner =>
"Adds gem to the Gemfile but does not install it"
method_option "optimistic", :type => :boolean, :banner => "Adds optimistic declaration of version to gem"
method_option "strict", :type => :boolean, :banner => "Adds strict declaration of version to gem"
def add(*gems)
require_relative "cli/add"
Add.new(options.dup, gems).run
end
desc "outdated GEM [OPTIONS]", "List installed gems with newer versions available"
long_desc <<-D
Outdated lists the names and versions of gems that have a newer version available
in the given source. Calling outdated with [GEM [GEM]] will only check for newer
versions of the given gems. Prerelease gems are ignored by default. If your gems
are up to date, Bundler will exit with a status of 0. Otherwise, it will exit 1.
For more information on patch level options (--major, --minor, --patch,
--strict) see documentation on the same options on the update command.
D
method_option "group", :type => :string, :banner => "List gems from a specific group"
method_option "groups", :type => :boolean, :banner => "List gems organized by groups"
method_option "local", :type => :boolean, :banner =>
"Do not attempt to fetch gems remotely and use the gem cache instead"
method_option "pre", :type => :boolean, :banner => "Check for newer pre-release gems"
method_option "source", :type => :array, :banner => "Check against a specific source"
method_option "filter-strict", :type => :boolean, :aliases => "--strict", :banner =>
"Only list newer versions allowed by your Gemfile requirements"
method_option "update-strict", :type => :boolean, :banner =>
"Strict conservative resolution, do not allow any gem to be updated past latest --patch | --minor | --major"
method_option "minor", :type => :boolean, :banner => "Prefer updating only to next minor version"
method_option "major", :type => :boolean, :banner => "Prefer updating to next major version (default)"
method_option "patch", :type => :boolean, :banner => "Prefer updating only to next patch version"
method_option "filter-major", :type => :boolean, :banner => "Only list major newer versions"
method_option "filter-minor", :type => :boolean, :banner => "Only list minor newer versions"
method_option "filter-patch", :type => :boolean, :banner => "Only list patch newer versions"
method_option "parseable", :aliases => "--porcelain", :type => :boolean, :banner =>
"Use minimal formatting for more parseable output"
method_option "only-explicit", :type => :boolean, :banner =>
"Only list gems specified in your Gemfile, not their dependencies"
def outdated(*gems)
require_relative "cli/outdated"
Outdated.new(options, gems).run
end
desc "fund [OPTIONS]", "Lists information about gems seeking funding assistance"
method_option "group", :aliases => "-g", :type => :array, :banner =>
"Fetch funding information for a specific group"
def fund
require_relative "cli/fund"
Fund.new(options).run
end
desc "cache [OPTIONS]", "Locks and then caches all of the gems into vendor/cache"
method_option "all", :type => :boolean,
:default => Bundler.feature_flag.cache_all?,
:banner => "Include all sources (including path and git)."
method_option "all-platforms", :type => :boolean, :banner => "Include gems for all platforms present in the lockfile, not only the current one"
method_option "cache-path", :type => :string, :banner =>
"Specify a different cache path than the default (vendor/cache)."
method_option "gemfile", :type => :string, :banner => "Use the specified gemfile instead of Gemfile"
method_option "no-install", :type => :boolean, :banner => "Don't install the gems, only update the cache."
method_option "no-prune", :type => :boolean, :banner => "Don't remove stale gems from the cache."
method_option "path", :type => :string, :banner =>
"Specify a different path than the system default ($BUNDLE_PATH or $GEM_HOME).#{" Bundler will remember this value for future installs on this machine" unless Bundler.feature_flag.forget_cli_options?}"
method_option "quiet", :type => :boolean, :banner => "Only output warnings and errors."
method_option "frozen", :type => :boolean, :banner =>
"Do not allow the Gemfile.lock to be updated after this bundle cache operation's install"
long_desc <<-D
The cache command will copy the .gem files for every gem in the bundle into the
directory ./vendor/cache. If you then check that directory into your source
control repository, others who check out your source will be able to install the
bundle without having to download any additional gems.
D
def cache
SharedHelpers.major_deprecation 2,
"The `--all` flag is deprecated because it relies on being " \
"remembered across bundler invocations, which bundler will no longer " \
"do in future versions. Instead please use `bundle config set cache_all true`, " \
"and stop using this flag" if ARGV.include?("--all")
SharedHelpers.major_deprecation 2,
"The `--path` flag is deprecated because its semantics are unclear. " \
"Use `bundle config cache_path` to configure the path of your cache of gems, " \
"and `bundle config path` to configure the path where your gems are installed, " \
"and stop using this flag" if ARGV.include?("--path")
require_relative "cli/cache"
Cache.new(options).run
end
map aliases_for("cache")
desc "exec [OPTIONS]", "Run the command in context of the bundle"
method_option :keep_file_descriptors, :type => :boolean, :default => true
method_option :gemfile, :type => :string, :required => false
long_desc <<-D
Exec runs a command, providing it access to the gems in the bundle. While using
bundle exec you can require and call the bundled gems as if they were installed
into the system wide RubyGems repository.
D
def exec(*args)
if ARGV.include?("--no-keep-file-descriptors")
SharedHelpers.major_deprecation(2, "The `--no-keep-file-descriptors` has been deprecated. `bundle exec` no longer mess with your file descriptors. Close them in the exec'd script if you need to")
end
require_relative "cli/exec"
Exec.new(options, args).run
end
map aliases_for("exec")
desc "config NAME [VALUE]", "Retrieve or set a configuration value"
long_desc <<-D
Retrieves or sets a configuration value. If only one parameter is provided, retrieve the value. If two parameters are provided, replace the
existing value with the newly provided one.
By default, setting a configuration value sets it for all projects
on the machine.
If a global setting is superseded by local configuration, this command
will show the current value, as well as any superseded values and
where they were specified.
D
require_relative "cli/config"
subcommand "config", Config
desc "open GEM", "Opens the source directory of the given bundled gem"
method_option "path", :type => :string, :lazy_default => "", :banner => "Open relative path of the gem source."
def open(name)
require_relative "cli/open"
Open.new(options, name).run
end
unless Bundler.feature_flag.bundler_3_mode?
desc "console [GROUP]", "Opens an IRB session with the bundle pre-loaded"
def console(group = nil)
require_relative "cli/console"
Console.new(options, group).run
end
end
desc "version", "Prints Bundler version information"
def version
cli_help = current_command.name == "cli_help"
if cli_help || ARGV.include?("version")
build_info = " (#{BuildMetadata.built_at} commit #{BuildMetadata.git_commit_sha})"
end
if !cli_help && Bundler.feature_flag.print_only_version_number?
Bundler.ui.info "#{Bundler::VERSION}#{build_info}"
else
Bundler.ui.info "Bundler version #{Bundler::VERSION}#{build_info}"
end
end
map aliases_for("version")
desc "licenses", "Prints the license of all gems in the bundle"
def licenses
Bundler.load.specs.sort_by {|s| s.license.to_s }.reverse_each do |s|
gem_name = s.name
license = s.license || s.licenses
if license.empty?
Bundler.ui.warn "#{gem_name}: Unknown"
else
Bundler.ui.info "#{gem_name}: #{license}"
end
end
end
unless Bundler.feature_flag.bundler_3_mode?
desc "viz [OPTIONS]", "Generates a visual dependency graph", :hide => true
long_desc <<-D
Viz generates a PNG file of the current Gemfile as a dependency graph.
Viz requires the ruby-graphviz gem (and its dependencies).
The associated gems must also be installed via 'bundle install'.
D
method_option :file, :type => :string, :default => "gem_graph", :aliases => "-f", :desc => "The name to use for the generated file. see format option"
method_option :format, :type => :string, :default => "png", :aliases => "-F", :desc => "This is output format option. Supported format is png, jpg, svg, dot ..."
method_option :requirements, :type => :boolean, :default => false, :aliases => "-R", :desc => "Set to show the version of each required dependency."
method_option :version, :type => :boolean, :default => false, :aliases => "-v", :desc => "Set to show each gem version."
method_option :without, :type => :array, :default => [], :aliases => "-W", :banner => "GROUP[ GROUP...]", :desc => "Exclude gems that are part of the specified named group."
def viz
SharedHelpers.major_deprecation 2, "The `viz` command has been renamed to `graph` and moved to a plugin. See https://github.com/rubygems/bundler-graph"
require_relative "cli/viz"
Viz.new(options.dup).run
end
end
old_gem = instance_method(:gem)
desc "gem NAME [OPTIONS]", "Creates a skeleton for creating a rubygem"
method_option :exe, :type => :boolean, :default => false, :aliases => ["--bin", "-b"], :desc => "Generate a binary executable for your library."
method_option :coc, :type => :boolean, :desc => "Generate a code of conduct file. Set a default with `bundle config set --global gem.coc true`."
method_option :edit, :type => :string, :aliases => "-e", :required => false, :banner => "EDITOR",
:lazy_default => [ENV["BUNDLER_EDITOR"], ENV["VISUAL"], ENV["EDITOR"]].find {|e| !e.nil? && !e.empty? },
:desc => "Open generated gemspec in the specified editor (defaults to $EDITOR or $BUNDLER_EDITOR)"
method_option :ext, :type => :string, :desc => "Generate the boilerplate for C extension code.", :enum => EXTENSIONS
method_option :git, :type => :boolean, :default => true, :desc => "Initialize a git repo inside your library."
method_option :mit, :type => :boolean, :desc => "Generate an MIT license file. Set a default with `bundle config set --global gem.mit true`."
method_option :rubocop, :type => :boolean, :desc => "Add rubocop to the generated Rakefile and gemspec. Set a default with `bundle config set --global gem.rubocop true`."
method_option :changelog, :type => :boolean, :desc => "Generate changelog file. Set a default with `bundle config set --global gem.changelog true`."
method_option :test, :type => :string, :lazy_default => Bundler.settings["gem.test"] || "", :aliases => "-t", :banner => "Use the specified test framework for your library",
:desc => "Generate a test directory for your library, either rspec, minitest or test-unit. Set a default with `bundle config set --global gem.test (rspec|minitest|test-unit)`."
method_option :ci, :type => :string, :lazy_default => Bundler.settings["gem.ci"] || "",
:desc => "Generate CI configuration, either GitHub Actions, GitLab CI or CircleCI. Set a default with `bundle config set --global gem.ci (github|gitlab|circle)`"
method_option :linter, :type => :string, :lazy_default => Bundler.settings["gem.linter"] || "",
:desc => "Add a linter and code formatter, either RuboCop or Standard. Set a default with `bundle config set --global gem.linter (rubocop|standard)`"
method_option :github_username, :type => :string, :default => Bundler.settings["gem.github_username"], :banner => "Set your username on GitHub", :desc => "Fill in GitHub username on README so that you don't have to do it manually. Set a default with `bundle config set --global gem.github_username <your_username>`."
def gem(name)
end
commands["gem"].tap do |gem_command|
def gem_command.run(instance, args = [])
arity = 1 # name
require_relative "cli/gem"
cmd_args = args + [instance]
cmd_args.unshift(instance.options)
cmd = begin
Gem.new(*cmd_args)
rescue ArgumentError => e
instance.class.handle_argument_error(self, e, args, arity)
end
cmd.run
end
end
undef_method(:gem)
define_method(:gem, old_gem)
private :gem
def self.source_root
File.expand_path("templates", __dir__)
end
desc "clean [OPTIONS]", "Cleans up unused gems in your bundler directory", :hide => true
method_option "dry-run", :type => :boolean, :default => false, :banner =>
"Only print out changes, do not clean gems"
method_option "force", :type => :boolean, :default => false, :banner =>
"Forces cleaning up unused gems even if Bundler is configured to use globally installed gems. As a consequence, removes all system gems except for the ones in the current application."
def clean
require_relative "cli/clean"
Clean.new(options.dup).run
end
desc "platform [OPTIONS]", "Displays platform compatibility information"
method_option "ruby", :type => :boolean, :default => false, :banner =>
"only display ruby related platform information"
def platform
require_relative "cli/platform"
Platform.new(options).run
end
desc "inject GEM VERSION", "Add the named gem, with version requirements, to the resolved Gemfile", :hide => true
method_option "source", :type => :string, :banner =>
"Install gem from the given source"
method_option "group", :type => :string, :banner =>
"Install gem into a bundler group"
def inject(name, version)
SharedHelpers.major_deprecation 2, "The `inject` command has been replaced by the `add` command"
require_relative "cli/inject"
Inject.new(options.dup, name, version).run
end
desc "lock", "Creates a lockfile without installing"
method_option "update", :type => :array, :lazy_default => true, :banner =>
"ignore the existing lockfile, update all gems by default, or update list of given gems"
method_option "local", :type => :boolean, :default => false, :banner =>
"do not attempt to fetch remote gemspecs and use the local gem cache only"
method_option "print", :type => :boolean, :default => false, :banner =>
"print the lockfile to STDOUT instead of writing to the file system"
method_option "gemfile", :type => :string, :banner =>
"Use the specified gemfile instead of Gemfile"
method_option "lockfile", :type => :string, :default => nil, :banner =>
"the path the lockfile should be written to"
method_option "full-index", :type => :boolean, :default => false, :banner =>
"Fall back to using the single-file index of all gems"
method_option "add-platform", :type => :array, :default => [], :banner =>
"Add a new platform to the lockfile"
method_option "remove-platform", :type => :array, :default => [], :banner =>
"Remove a platform from the lockfile"
method_option "patch", :type => :boolean, :banner =>
"If updating, prefer updating only to next patch version"
method_option "minor", :type => :boolean, :banner =>
"If updating, prefer updating only to next minor version"
method_option "major", :type => :boolean, :banner =>
"If updating, prefer updating to next major version (default)"
method_option "pre", :type => :boolean, :banner =>
"If updating, always choose the highest allowed version, regardless of prerelease status"
method_option "strict", :type => :boolean, :banner =>
"If updating, do not allow any gem to be updated past latest --patch | --minor | --major"
method_option "conservative", :type => :boolean, :banner =>
"If updating, use bundle install conservative update behavior and do not allow shared dependencies to be updated"
method_option "bundler", :type => :string, :lazy_default => "> 0.a", :banner =>
"Update the locked version of bundler"
def lock
require_relative "cli/lock"
Lock.new(options).run
end
desc "env", "Print information about the environment Bundler is running under"
def env
Env.write($stdout)
end
desc "doctor [OPTIONS]", "Checks the bundle for common problems"
long_desc <<-D
Doctor scans the OS dependencies of each of the gems requested in the Gemfile. If
missing dependencies are detected, Bundler prints them and exits status 1.
Otherwise, Bundler prints a success message and exits with a status of 0.
D
method_option "gemfile", :type => :string, :banner =>
"Use the specified gemfile instead of Gemfile"
method_option "quiet", :type => :boolean, :banner =>
"Only output warnings and errors."
def doctor
require_relative "cli/doctor"
Doctor.new(options).run
end
desc "issue", "Learn how to report an issue in Bundler"
def issue
require_relative "cli/issue"
Issue.new.run
end
desc "pristine [GEMS...]", "Restores installed gems to pristine condition"
long_desc <<-D
Restores installed gems to pristine condition from files located in the
gem cache. Gems installed from a git repository will be issued `git
checkout --force`.
D
def pristine(*gems)
require_relative "cli/pristine"
Pristine.new(gems).run
end
if Bundler.feature_flag.plugins?
require_relative "cli/plugin"
desc "plugin", "Manage the bundler plugins"
subcommand "plugin", Plugin
end
# Reformat the arguments passed to bundle that include a --help flag
# into the corresponding `bundle help #{command}` call
def self.reformatted_help_args(args)
bundler_commands = (COMMAND_ALIASES.keys + COMMAND_ALIASES.values).flatten
help_flags = %w[--help -h]
exec_commands = ["exec"] + COMMAND_ALIASES["exec"]
help_used = args.index {|a| help_flags.include? a }
exec_used = args.index {|a| exec_commands.include? a }
command = args.find {|a| bundler_commands.include? a }
command = all_aliases[command] if all_aliases[command]
if exec_used && help_used
if exec_used + help_used == 1
%w[help exec]
else
args
end
elsif help_used
args = args.dup
args.delete_at(help_used)
["help", command || args].flatten.compact
else
args
end
end
def self.check_deprecated_ext_option(arguments)
# when deprecated version of `--ext` is called
# print out deprecation warning and pretend `--ext=c` was provided
if deprecated_ext_value?(arguments)
SharedHelpers.major_deprecation 2, "Extensions can now be generated using C or Rust, so `--ext` with no arguments has been deprecated. Please select a language, e.g. `--ext=rust` to generate a Rust extension. This gem will now be generated as if `--ext=c` was used."
arguments[arguments.index("--ext")] = "--ext=c"
end
end
def self.deprecated_ext_value?(arguments)
index = arguments.index("--ext")
next_argument = arguments[index+1]
# it is ok when --ext is followed with valid extension value
# for example `bundle gem hello --ext c`
return false if EXTENSIONS.include?(next_argument)
# deprecated call when --ext is called with no value in last position
# for example `bundle gem hello_gem --ext`
return true if next_argument.nil?
# deprecated call when --ext is followed by other parameter
# for example `bundle gem --ext --no-ci hello_gem`
return true if next_argument.start_with?("-")
# deprecated call when --ext is followed by gem name
# for example `bundle gem --ext hello_gem`
return true if next_argument
false
end
private
# Automatically invoke `bundle install` and resume if
# Bundler.settings[:auto_install] exists. This is set through config cmd
# `bundle config set --global auto_install 1`.
#
# Note that this method `nil`s out the global Definition object, so it
# should be called first, before you instantiate anything like an
# `Installer` that'll keep a reference to the old one instead.
def auto_install
return unless Bundler.settings[:auto_install]
begin
Bundler.definition.specs
rescue GemNotFound
Bundler.ui.info "Automatically installing missing gems."
Bundler.reset!
invoke :install, []
Bundler.reset!
end
end
def current_command
_, _, config = @_initializer
config[:current_command]
end
def print_command
return unless Bundler.ui.debug?
cmd = current_command
command_name = cmd.name
return if PARSEABLE_COMMANDS.include?(command_name)
command = ["bundle", command_name] + args
options_to_print = options.dup
options_to_print.delete_if do |k, v|
next unless o = cmd.options[k]
o.default == v
end
command << Thor::Options.to_switches(options_to_print.sort_by(&:first)).strip
command.reject!(&:empty?)
Bundler.ui.info "Running `#{command * " "}` with bundler #{Bundler::VERSION}"
end
def warn_on_outdated_bundler
return if Bundler.settings[:disable_version_check]
command_name = current_command.name
return if PARSEABLE_COMMANDS.include?(command_name)
return unless SharedHelpers.md5_available?
latest = Fetcher::CompactIndex.
new(nil, Source::Rubygems::Remote.new(Bundler::URI("https://rubygems.org")), nil).
send(:compact_index_client).
instance_variable_get(:@cache).
dependencies("bundler").
map {|d| Gem::Version.new(d.first) }.
max
return unless latest
current = Gem::Version.new(VERSION)
return if current >= latest
Bundler.ui.warn \
"The latest bundler is #{latest}, but you are currently running #{current}.\n" \
"To update to the most recent version, run `bundle update --bundler`"
rescue RuntimeError
nil
end
def remembered_negative_flag_deprecation(name)
positive_name = name.gsub(/\Ano-/, "")
option = current_command.options[positive_name]
flag_name = "--no-" + option.switch_name.gsub(/\A--/, "")
flag_deprecation(positive_name, flag_name, option)
end
def remembered_flag_deprecation(name)
option = current_command.options[name]
flag_name = option.switch_name
flag_deprecation(name, flag_name, option)
end
def flag_deprecation(name, flag_name, option)
name_index = ARGV.find {|arg| flag_name == arg.split("=")[0] }
return unless name_index
value = options[name]
value = value.join(" ").to_s if option.type == :array
Bundler::SharedHelpers.major_deprecation 2,
"The `#{flag_name}` flag is deprecated because it relies on being " \
"remembered across bundler invocations, which bundler will no longer " \
"do in future versions. Instead please use `bundle config set --local #{name.tr("-", "_")} " \
"'#{value}'`, and stop using this flag"
end
end
end

View File

@@ -1,47 +0,0 @@
# frozen_string_literal: true
module Bundler
class CLI::Add
attr_reader :gems, :options, :version
def initialize(options, gems)
@gems = gems
@options = options
@options[:group] = options[:group].split(",").map(&:strip) unless options[:group].nil?
@version = options[:version].split(",").map(&:strip) unless options[:version].nil?
end
def run
validate_options!
inject_dependencies
perform_bundle_install unless options["skip-install"]
end
private
def perform_bundle_install
Installer.install(Bundler.root, Bundler.definition)
Bundler.load.cache if Bundler.app_cache.exist?
end
def inject_dependencies
dependencies = gems.map {|g| Bundler::Dependency.new(g, version, options) }
Injector.inject(dependencies,
:conservative_versioning => options[:version].nil?, # Perform conservative versioning only when version is not specified
:optimistic => options[:optimistic],
:strict => options[:strict])
end
def validate_options!
raise InvalidOption, "You can not specify `--strict` and `--optimistic` at the same time." if options[:strict] && options[:optimistic]
# raise error when no gems are specified
raise InvalidOption, "Please specify gems to add." if gems.empty?
version.to_a.each do |v|
raise InvalidOption, "Invalid gem requirement pattern '#{v}'" unless Gem::Requirement::PATTERN.match?(v.to_s)
end
end
end
end

View File

@@ -1,57 +0,0 @@
# frozen_string_literal: true
module Bundler
class CLI::Binstubs
attr_reader :options, :gems
def initialize(options, gems)
@options = options
@gems = gems
end
def run
Bundler.definition.validate_runtime!
path_option = options["path"]
path_option = nil if path_option&.empty?
Bundler.settings.set_command_option :bin, path_option if options["path"]
Bundler.settings.set_command_option_if_given :shebang, options["shebang"]
installer = Installer.new(Bundler.root, Bundler.definition)
installer_opts = {
:force => options[:force],
:binstubs_cmd => true,
:all_platforms => options["all-platforms"],
}
if options[:all]
raise InvalidOption, "Cannot specify --all with specific gems" unless gems.empty?
@gems = Bundler.definition.specs.map(&:name)
installer_opts.delete(:binstubs_cmd)
elsif gems.empty?
Bundler.ui.error "`bundle binstubs` needs at least one gem to run."
exit 1
end
gems.each do |gem_name|
spec = Bundler.definition.specs.find {|s| s.name == gem_name }
unless spec
raise GemNotFound, Bundler::CLI::Common.gem_not_found_message(
gem_name, Bundler.definition.specs
)
end
if options[:standalone]
if gem_name == "bundler"
Bundler.ui.warn("Sorry, Bundler can only be run via RubyGems.") unless options[:all]
next
end
Bundler.settings.temporary(:path => (Bundler.settings[:path] || Bundler.root)) do
installer.generate_standalone_bundler_executable_stubs(spec, installer_opts)
end
else
installer.generate_bundler_executable_stubs(spec, installer_opts)
end
end
end
end
end

View File

@@ -1,43 +0,0 @@
# frozen_string_literal: true
module Bundler
class CLI::Cache
attr_reader :options
def initialize(options)
@options = options
end
def run
Bundler.ui.level = "warn" if options[:quiet]
Bundler.settings.set_command_option_if_given :path, options[:path]
Bundler.settings.set_command_option_if_given :cache_path, options["cache-path"]
setup_cache_all
install
# TODO: move cache contents here now that all bundles are locked
custom_path = Bundler.settings[:path] if options[:path]
Bundler.settings.temporary(:cache_all_platforms => options["all-platforms"]) do
Bundler.load.cache(custom_path)
end
end
private
def install
require_relative "install"
options = self.options.dup
options["local"] = false if Bundler.settings[:cache_all_platforms]
options["no-cache"] = true
Bundler::CLI::Install.new(options).run
end
def setup_cache_all
all = options.fetch(:all, Bundler.feature_flag.cache_all? || nil)
Bundler.settings.set_command_option_if_given :cache_all, all
end
end
end

View File

@@ -1,40 +0,0 @@
# frozen_string_literal: true
module Bundler
class CLI::Check
attr_reader :options
def initialize(options)
@options = options
end
def run
Bundler.settings.set_command_option_if_given :path, options[:path]
definition = Bundler.definition
definition.validate_runtime!
begin
definition.resolve_only_locally!
not_installed = definition.missing_specs
rescue GemNotFound, SolveFailure
Bundler.ui.error "Bundler can't satisfy your Gemfile's dependencies."
Bundler.ui.warn "Install missing gems with `bundle install`."
exit 1
end
if not_installed.any?
Bundler.ui.error "The following gems are missing"
not_installed.each {|s| Bundler.ui.error " * #{s.name} (#{s.version})" }
Bundler.ui.warn "Install missing gems with `bundle install`"
exit 1
elsif !Bundler.default_lockfile.file? && Bundler.frozen_bundle?
Bundler.ui.error "This bundle has been frozen, but there is no #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)} present"
exit 1
else
Bundler.load.lock(:preserve_unknown_sections => true) unless options[:"dry-run"]
Bundler.ui.info "The Gemfile's dependencies are satisfied"
end
end
end
end

View File

@@ -1,25 +0,0 @@
# frozen_string_literal: true
module Bundler
class CLI::Clean
attr_reader :options
def initialize(options)
@options = options
end
def run
require_path_or_force unless options[:"dry-run"]
Bundler.load.clean(options[:"dry-run"])
end
protected
def require_path_or_force
return unless Bundler.use_system_gems? && !options[:force]
raise InvalidOption, "Cleaning all the gems on your system is dangerous! " \
"If you're sure you want to remove every system gem not in this " \
"bundle, run `bundle clean --force`."
end
end
end

View File

@@ -1,130 +0,0 @@
# frozen_string_literal: true
module Bundler
module CLI::Common
def self.output_post_install_messages(messages)
return if Bundler.settings["ignore_messages"]
messages.to_a.each do |name, msg|
print_post_install_message(name, msg) unless Bundler.settings["ignore_messages.#{name}"]
end
end
def self.print_post_install_message(name, msg)
Bundler.ui.confirm "Post-install message from #{name}:"
Bundler.ui.info msg
end
def self.output_fund_metadata_summary
return if Bundler.settings["ignore_funding_requests"]
definition = Bundler.definition
current_dependencies = definition.requested_dependencies
current_specs = definition.specs
count = current_dependencies.count {|dep| current_specs[dep.name].first.metadata.key?("funding_uri") }
return if count.zero?
intro = count > 1 ? "#{count} installed gems you directly depend on are" : "#{count} installed gem you directly depend on is"
message = "#{intro} looking for funding.\n Run `bundle fund` for details"
Bundler.ui.info message
end
def self.output_without_groups_message(command)
return if Bundler.settings[:without].empty?
Bundler.ui.confirm without_groups_message(command)
end
def self.without_groups_message(command)
command_in_past_tense = command == :install ? "installed" : "updated"
groups = Bundler.settings[:without]
"Gems in the #{verbalize_groups(groups)} were not #{command_in_past_tense}."
end
def self.verbalize_groups(groups)
groups.map! {|g| "'#{g}'" }
group_list = [groups[0...-1].join(", "), groups[-1..-1]].
reject {|s| s.to_s.empty? }.join(" and ")
group_str = groups.size == 1 ? "group" : "groups"
"#{group_str} #{group_list}"
end
def self.select_spec(name, regex_match = nil)
specs = []
regexp = Regexp.new(name) if regex_match
Bundler.definition.specs.each do |spec|
return spec if spec.name == name
specs << spec if regexp && spec.name =~ regexp
end
case specs.count
when 0
dep_in_other_group = Bundler.definition.current_dependencies.find {|dep|dep.name == name }
if dep_in_other_group
raise GemNotFound, "Could not find gem '#{name}', because it's in the #{verbalize_groups(dep_in_other_group.groups)}, configured to be ignored."
else
raise GemNotFound, gem_not_found_message(name, Bundler.definition.dependencies)
end
when 1
specs.first
else
ask_for_spec_from(specs)
end
rescue RegexpError
raise GemNotFound, gem_not_found_message(name, Bundler.definition.dependencies)
end
def self.ask_for_spec_from(specs)
specs.each_with_index do |spec, index|
Bundler.ui.info "#{index.succ} : #{spec.name}", true
end
Bundler.ui.info "0 : - exit -", true
num = Bundler.ui.ask("> ").to_i
num > 0 ? specs[num - 1] : nil
end
def self.gem_not_found_message(missing_gem_name, alternatives)
require_relative "../similarity_detector"
message = "Could not find gem '#{missing_gem_name}'."
alternate_names = alternatives.map {|a| a.respond_to?(:name) ? a.name : a }
suggestions = SimilarityDetector.new(alternate_names).similar_word_list(missing_gem_name)
message += "\nDid you mean #{suggestions}?" if suggestions
message
end
def self.ensure_all_gems_in_lockfile!(names, locked_gems = Bundler.locked_gems)
return unless locked_gems
locked_names = locked_gems.specs.map(&:name).uniq
names.-(locked_names).each do |g|
raise GemNotFound, gem_not_found_message(g, locked_names)
end
end
def self.configure_gem_version_promoter(definition, options)
patch_level = patch_level_options(options)
patch_level << :patch if patch_level.empty? && Bundler.settings[:prefer_patch]
raise InvalidOption, "Provide only one of the following options: #{patch_level.join(", ")}" unless patch_level.length <= 1
definition.gem_version_promoter.tap do |gvp|
gvp.level = patch_level.first || :major
gvp.strict = options[:strict] || options["filter-strict"]
gvp.pre = options[:pre]
end
end
def self.patch_level_options(options)
[:major, :minor, :patch].select {|v| options.keys.include?(v.to_s) }
end
def self.clean_after_install?
clean = Bundler.settings[:clean]
return clean unless clean.nil?
clean ||= Bundler.feature_flag.auto_clean_without_path? && Bundler.settings[:path].nil?
clean &&= !Bundler.use_system_gems?
clean
end
end
end

View File

@@ -1,203 +0,0 @@
# frozen_string_literal: true
module Bundler
class CLI::Config < Thor
class_option :parseable, :type => :boolean, :banner => "Use minimal formatting for more parseable output"
def self.scope_options
method_option :global, :type => :boolean, :banner => "Only change the global config"
method_option :local, :type => :boolean, :banner => "Only change the local config"
end
private_class_method :scope_options
desc "base NAME [VALUE]", "The Bundler 1 config interface", :hide => true
scope_options
method_option :delete, :type => :boolean, :banner => "delete"
def base(name = nil, *value)
new_args =
if ARGV.size == 1
["config", "list"]
elsif ARGV.include?("--delete")
ARGV.map {|arg| arg == "--delete" ? "unset" : arg }
elsif ARGV.include?("--global") || ARGV.include?("--local") || ARGV.size == 3
["config", "set", *ARGV[1..-1]]
else
["config", "get", ARGV[1]]
end
SharedHelpers.major_deprecation 3,
"Using the `config` command without a subcommand [list, get, set, unset] is deprecated and will be removed in the future. Use `bundle #{new_args.join(" ")}` instead."
Base.new(options, name, value, self).run
end
desc "list", "List out all configured settings"
def list
Base.new(options, nil, nil, self).run
end
desc "get NAME", "Returns the value for the given key"
def get(name)
Base.new(options, name, nil, self).run
end
desc "set NAME VALUE", "Sets the given value for the given key"
scope_options
def set(name, value, *value_)
Base.new(options, name, value_.unshift(value), self).run
end
desc "unset NAME", "Unsets the value for the given key"
scope_options
def unset(name)
options[:delete] = true
Base.new(options, name, nil, self).run
end
default_task :base
class Base
attr_reader :name, :value, :options, :scope, :thor
def initialize(options, name, value, thor)
@options = options
@name = name
value = Array(value)
@value = value.empty? ? nil : value.join(" ")
@thor = thor
validate_scope!
end
def run
unless name
warn_unused_scope "Ignoring --#{scope}"
confirm_all
return
end
if options[:delete]
if !explicit_scope? || scope != "global"
Bundler.settings.set_local(name, nil)
end
if !explicit_scope? || scope != "local"
Bundler.settings.set_global(name, nil)
end
return
end
if value.nil?
warn_unused_scope "Ignoring --#{scope} since no value to set was given"
if options[:parseable]
if value = Bundler.settings[name]
Bundler.ui.info("#{name}=#{value}")
end
return
end
confirm(name)
return
end
Bundler.ui.info(message) if message
Bundler.settings.send("set_#{scope}", name, new_value)
end
def confirm_all
if @options[:parseable]
thor.with_padding do
Bundler.settings.all.each do |setting|
val = Bundler.settings[setting]
Bundler.ui.info "#{setting}=#{val}"
end
end
else
Bundler.ui.confirm "Settings are listed in order of priority. The top value will be used.\n"
Bundler.settings.all.each do |setting|
Bundler.ui.confirm setting
show_pretty_values_for(setting)
Bundler.ui.confirm ""
end
end
end
def confirm(name)
Bundler.ui.confirm "Settings for `#{name}` in order of priority. The top value will be used"
show_pretty_values_for(name)
end
def new_value
pathname = Pathname.new(value)
if name.start_with?("local.") && pathname.directory?
pathname.expand_path.to_s
else
value
end
end
def message
locations = Bundler.settings.locations(name)
if @options[:parseable]
"#{name}=#{new_value}" if new_value
elsif scope == "global"
if !locations[:local].nil?
"Your application has set #{name} to #{locations[:local].inspect}. " \
"This will override the global value you are currently setting"
elsif locations[:env]
"You have a bundler environment variable for #{name} set to " \
"#{locations[:env].inspect}. This will take precedence over the global value you are setting"
elsif !locations[:global].nil? && locations[:global] != value
"You are replacing the current global value of #{name}, which is currently " \
"#{locations[:global].inspect}"
end
elsif scope == "local" && !locations[:local].nil? && locations[:local] != value
"You are replacing the current local value of #{name}, which is currently " \
"#{locations[:local].inspect}"
end
end
def show_pretty_values_for(setting)
thor.with_padding do
Bundler.settings.pretty_values_for(setting).each do |line|
Bundler.ui.info line
end
end
end
def explicit_scope?
@explicit_scope
end
def warn_unused_scope(msg)
return unless explicit_scope?
return if options[:parseable]
Bundler.ui.warn(msg)
end
def validate_scope!
@explicit_scope = true
scopes = %w[global local].select {|s| options[s] }
case scopes.size
when 0
@scope = inside_app? ? "local" : "global"
@explicit_scope = false
when 1
@scope = scopes.first
else
raise InvalidOption,
"The options #{scopes.join " and "} were specified. Please only use one of the switches at a time."
end
end
private
def inside_app?
Bundler.root
true
rescue GemfileNotFound
false
end
end
end
end

View File

@@ -1,43 +0,0 @@
# frozen_string_literal: true
module Bundler
class CLI::Console
attr_reader :options, :group
def initialize(options, group)
@options = options
@group = group
end
def run
Bundler::SharedHelpers.major_deprecation 2, "bundle console will be replaced " \
"by `bin/console` generated by `bundle gem <name>`"
group ? Bundler.require(:default, *group.split(" ").map!(&:to_sym)) : Bundler.require
ARGV.clear
console = get_console(Bundler.settings[:console] || "irb")
console.start
end
def get_console(name)
require name
get_constant(name)
rescue LoadError
Bundler.ui.error "Couldn't load console #{name}, falling back to irb"
require "irb"
get_constant("irb")
end
def get_constant(name)
const_name = {
"pry" => :Pry,
"ripl" => :Ripl,
"irb" => :IRB,
}[name]
Object.const_get(const_name)
rescue NameError
Bundler.ui.error "Could not find constant #{const_name}"
exit 1
end
end
end

View File

@@ -1,157 +0,0 @@
# frozen_string_literal: true
require "rbconfig"
require "shellwords"
require "fiddle"
module Bundler
class CLI::Doctor
DARWIN_REGEX = /\s+(.+) \(compatibility /.freeze
LDD_REGEX = /\t\S+ => (\S+) \(\S+\)/.freeze
attr_reader :options
def initialize(options)
@options = options
end
def otool_available?
Bundler.which("otool")
end
def ldd_available?
Bundler.which("ldd")
end
def dylibs_darwin(path)
output = `/usr/bin/otool -L #{path.shellescape}`.chomp
dylibs = output.split("\n")[1..-1].map {|l| l.match(DARWIN_REGEX).captures[0] }.uniq
# ignore @rpath and friends
dylibs.reject {|dylib| dylib.start_with? "@" }
end
def dylibs_ldd(path)
output = `/usr/bin/ldd #{path.shellescape}`.chomp
output.split("\n").map do |l|
match = l.match(LDD_REGEX)
next if match.nil?
match.captures[0]
end.compact
end
def dylibs(path)
case RbConfig::CONFIG["host_os"]
when /darwin/
return [] unless otool_available?
dylibs_darwin(path)
when /(linux|solaris|bsd)/
return [] unless ldd_available?
dylibs_ldd(path)
else # Windows, etc.
Bundler.ui.warn("Dynamic library check not supported on this platform.")
[]
end
end
def bundles_for_gem(spec)
Dir.glob("#{spec.full_gem_path}/**/*.bundle")
end
def check!
require_relative "check"
Bundler::CLI::Check.new({}).run
end
def run
Bundler.ui.level = "warn" if options[:quiet]
Bundler.settings.validate!
check!
definition = Bundler.definition
broken_links = {}
definition.specs.each do |spec|
bundles_for_gem(spec).each do |bundle|
bad_paths = dylibs(bundle).select do |f|
Fiddle.dlopen(f)
false
rescue Fiddle::DLError
true
end
if bad_paths.any?
broken_links[spec] ||= []
broken_links[spec].concat(bad_paths)
end
end
end
permissions_valid = check_home_permissions
if broken_links.any?
message = "The following gems are missing OS dependencies:"
broken_links.map do |spec, paths|
paths.uniq.map do |path|
"\n * #{spec.name}: #{path}"
end
end.flatten.sort.each {|m| message += m }
raise ProductionError, message
elsif !permissions_valid
Bundler.ui.info "No issues found with the installed bundle"
end
end
private
def check_home_permissions
require "find"
files_not_readable_or_writable = []
files_not_rw_and_owned_by_different_user = []
files_not_owned_by_current_user_but_still_rw = []
broken_symlinks = []
Find.find(Bundler.bundle_path.to_s).each do |f|
if !File.exist?(f)
broken_symlinks << f
elsif !File.writable?(f) || !File.readable?(f)
if File.stat(f).uid != Process.uid
files_not_rw_and_owned_by_different_user << f
else
files_not_readable_or_writable << f
end
elsif File.stat(f).uid != Process.uid
files_not_owned_by_current_user_but_still_rw << f
end
end
ok = true
if broken_symlinks.any?
Bundler.ui.warn "Broken links exist in the Bundler home. Please report them to the offending gem's upstream repo. These files are:\n - #{broken_symlinks.join("\n - ")}"
ok = false
end
if files_not_owned_by_current_user_but_still_rw.any?
Bundler.ui.warn "Files exist in the Bundler home that are owned by another " \
"user, but are still readable/writable. These files are:\n - #{files_not_owned_by_current_user_but_still_rw.join("\n - ")}"
ok = false
end
if files_not_rw_and_owned_by_different_user.any?
Bundler.ui.warn "Files exist in the Bundler home that are owned by another " \
"user, and are not readable/writable. These files are:\n - #{files_not_rw_and_owned_by_different_user.join("\n - ")}"
ok = false
end
if files_not_readable_or_writable.any?
Bundler.ui.warn "Files exist in the Bundler home that are not " \
"readable/writable by the current user. These files are:\n - #{files_not_readable_or_writable.join("\n - ")}"
ok = false
end
ok
end
end
end

View File

@@ -1,88 +0,0 @@
# frozen_string_literal: true
require_relative "../current_ruby"
module Bundler
class CLI::Exec
attr_reader :options, :args, :cmd
TRAPPED_SIGNALS = %w[INT].freeze
def initialize(options, args)
@options = options
@cmd = args.shift
@args = args
@args << { :close_others => !options.keep_file_descriptors? } unless Bundler.current_ruby.jruby?
end
def run
validate_cmd!
SharedHelpers.set_bundle_environment
if bin_path = Bundler.which(cmd)
if !Bundler.settings[:disable_exec_load] && ruby_shebang?(bin_path)
return kernel_load(bin_path, *args)
end
kernel_exec(bin_path, *args)
else
# exec using the given command
kernel_exec(cmd, *args)
end
end
private
def validate_cmd!
return unless cmd.nil?
Bundler.ui.error "bundler: exec needs a command to run"
exit 128
end
def kernel_exec(*args)
Kernel.exec(*args)
rescue Errno::EACCES, Errno::ENOEXEC
Bundler.ui.error "bundler: not executable: #{cmd}"
exit 126
rescue Errno::ENOENT
Bundler.ui.error "bundler: command not found: #{cmd}"
Bundler.ui.warn "Install missing gem executables with `bundle install`"
exit 127
end
def kernel_load(file, *args)
args.pop if args.last.is_a?(Hash)
ARGV.replace(args)
$0 = file
Process.setproctitle(process_title(file, args)) if Process.respond_to?(:setproctitle)
require_relative "../setup"
TRAPPED_SIGNALS.each {|s| trap(s, "DEFAULT") }
Kernel.load(file)
rescue SystemExit, SignalException
raise
rescue Exception # rubocop:disable Lint/RescueException
Bundler.ui.error "bundler: failed to load command: #{cmd} (#{file})"
Bundler::FriendlyErrors.disable!
raise
end
def process_title(file, args)
"#{file} #{args.join(" ")}".strip
end
def ruby_shebang?(file)
possibilities = [
"#!/usr/bin/env ruby\n",
"#!/usr/bin/env jruby\n",
"#!/usr/bin/env truffleruby\n",
"#!#{Gem.ruby}\n",
]
if File.zero?(file)
Bundler.ui.warn "#{file} is empty"
return false
end
first_line = File.open(file, "rb") {|f| f.read(possibilities.map(&:size).max) }
possibilities.any? {|shebang| first_line.start_with?(shebang) }
end
end
end

View File

@@ -1,36 +0,0 @@
# frozen_string_literal: true
module Bundler
class CLI::Fund
attr_reader :options
def initialize(options)
@options = options
end
def run
Bundler.definition.validate_runtime!
groups = Array(options[:group]).map(&:to_sym)
deps = if groups.any?
Bundler.definition.dependencies_for(groups)
else
Bundler.definition.current_dependencies
end
fund_info = deps.each_with_object([]) do |dep, arr|
spec = Bundler.definition.specs[dep.name].first
if spec.metadata.key?("funding_uri")
arr << "* #{spec.name} (#{spec.version})\n Funding: #{spec.metadata["funding_uri"]}"
end
end
if fund_info.empty?
Bundler.ui.info "None of the installed gems you directly depend on are looking for funding."
else
Bundler.ui.info fund_info.join("\n")
end
end
end
end

View File

@@ -1,465 +0,0 @@
# frozen_string_literal: true
require "pathname"
module Bundler
class CLI
Bundler.require_thor_actions
include Thor::Actions
end
class CLI::Gem
TEST_FRAMEWORK_VERSIONS = {
"rspec" => "3.0",
"minitest" => "5.0",
"test-unit" => "3.0",
}.freeze
attr_reader :options, :gem_name, :thor, :name, :target, :extension
def initialize(options, gem_name, thor)
@options = options
@gem_name = resolve_name(gem_name)
@thor = thor
thor.behavior = :invoke
thor.destination_root = nil
@name = @gem_name
@target = SharedHelpers.pwd.join(gem_name)
@extension = options[:ext]
validate_ext_name if @extension
validate_rust_builder_rubygems_version if @extension == "rust"
travis_removal_info
end
def run
Bundler.ui.confirm "Creating gem '#{name}'..."
underscored_name = name.tr("-", "_")
namespaced_path = name.tr("-", "/")
constant_name = name.gsub(/-[_-]*(?![_-]|$)/) { "::" }.gsub(/([_-]+|(::)|^)(.|$)/) { $2.to_s + $3.upcase }
constant_array = constant_name.split("::")
minitest_constant_name = constant_array.clone.tap {|a| a[-1] = "Test#{a[-1]}" }.join("::") # Foo::Bar => Foo::TestBar
use_git = Bundler.git_present? && options[:git]
git_author_name = use_git ? `git config user.name`.chomp : ""
git_username = use_git ? `git config github.user`.chomp : ""
git_user_email = use_git ? `git config user.email`.chomp : ""
github_username = if options[:github_username].nil?
git_username
elsif options[:github_username] == false
""
else
options[:github_username]
end
config = {
:name => name,
:underscored_name => underscored_name,
:namespaced_path => namespaced_path,
:makefile_path => "#{underscored_name}/#{underscored_name}",
:constant_name => constant_name,
:constant_array => constant_array,
:author => git_author_name.empty? ? "TODO: Write your name" : git_author_name,
:email => git_user_email.empty? ? "TODO: Write your email address" : git_user_email,
:test => options[:test],
:ext => extension,
:exe => options[:exe],
:bundler_version => bundler_dependency_version,
:git => use_git,
:github_username => github_username.empty? ? "[USERNAME]" : github_username,
:required_ruby_version => required_ruby_version,
:rust_builder_required_rubygems_version => rust_builder_required_rubygems_version,
:minitest_constant_name => minitest_constant_name,
}
ensure_safe_gem_name(name, constant_array)
templates = {
"#{Bundler.preferred_gemfile_name}.tt" => Bundler.preferred_gemfile_name,
"lib/newgem.rb.tt" => "lib/#{namespaced_path}.rb",
"lib/newgem/version.rb.tt" => "lib/#{namespaced_path}/version.rb",
"sig/newgem.rbs.tt" => "sig/#{namespaced_path}.rbs",
"newgem.gemspec.tt" => "#{name}.gemspec",
"Rakefile.tt" => "Rakefile",
"README.md.tt" => "README.md",
"bin/console.tt" => "bin/console",
"bin/setup.tt" => "bin/setup",
}
executables = %w[
bin/console
bin/setup
]
templates.merge!("gitignore.tt" => ".gitignore") if use_git
if test_framework = ask_and_set_test_framework
config[:test] = test_framework
config[:test_framework_version] = TEST_FRAMEWORK_VERSIONS[test_framework]
case test_framework
when "rspec"
templates.merge!(
"rspec.tt" => ".rspec",
"spec/spec_helper.rb.tt" => "spec/spec_helper.rb",
"spec/newgem_spec.rb.tt" => "spec/#{namespaced_path}_spec.rb"
)
config[:test_task] = :spec
when "minitest"
# Generate path for minitest target file (FileList["test/**/test_*.rb"])
# foo => test/test_foo.rb
# foo-bar => test/foo/test_bar.rb
# foo_bar => test/test_foo_bar.rb
paths = namespaced_path.rpartition("/")
paths[2] = "test_#{paths[2]}"
minitest_namespaced_path = paths.join("")
templates.merge!(
"test/minitest/test_helper.rb.tt" => "test/test_helper.rb",
"test/minitest/test_newgem.rb.tt" => "test/#{minitest_namespaced_path}.rb"
)
config[:test_task] = :test
when "test-unit"
templates.merge!(
"test/test-unit/test_helper.rb.tt" => "test/test_helper.rb",
"test/test-unit/newgem_test.rb.tt" => "test/#{namespaced_path}_test.rb"
)
config[:test_task] = :test
end
end
config[:ci] = ask_and_set_ci
case config[:ci]
when "github"
templates.merge!("github/workflows/main.yml.tt" => ".github/workflows/main.yml")
when "gitlab"
templates.merge!("gitlab-ci.yml.tt" => ".gitlab-ci.yml")
when "circle"
templates.merge!("circleci/config.yml.tt" => ".circleci/config.yml")
end
if ask_and_set(:mit, "Do you want to license your code permissively under the MIT license?",
"This means that any other developer or company will be legally allowed to use your code " \
"for free as long as they admit you created it. You can read more about the MIT license " \
"at https://choosealicense.com/licenses/mit.")
config[:mit] = true
Bundler.ui.info "MIT License enabled in config"
templates.merge!("LICENSE.txt.tt" => "LICENSE.txt")
end
if ask_and_set(:coc, "Do you want to include a code of conduct in gems you generate?",
"Codes of conduct can increase contributions to your project by contributors who " \
"prefer collaborative, safe spaces. You can read more about the code of conduct at " \
"contributor-covenant.org. Having a code of conduct means agreeing to the responsibility " \
"of enforcing it, so be sure that you are prepared to do that. Be sure that your email " \
"address is specified as a contact in the generated code of conduct so that people know " \
"who to contact in case of a violation. For suggestions about " \
"how to enforce codes of conduct, see https://bit.ly/coc-enforcement.")
config[:coc] = true
Bundler.ui.info "Code of conduct enabled in config"
templates.merge!("CODE_OF_CONDUCT.md.tt" => "CODE_OF_CONDUCT.md")
end
if ask_and_set(:changelog, "Do you want to include a changelog?",
"A changelog is a file which contains a curated, chronologically ordered list of notable " \
"changes for each version of a project. To make it easier for users and contributors to" \
" see precisely what notable changes have been made between each release (or version) of" \
" the project. Whether consumers or developers, the end users of software are" \
" human beings who care about what's in the software. When the software changes, people " \
"want to know why and how. see https://keepachangelog.com")
config[:changelog] = true
Bundler.ui.info "Changelog enabled in config"
templates.merge!("CHANGELOG.md.tt" => "CHANGELOG.md")
end
config[:linter] = ask_and_set_linter
case config[:linter]
when "rubocop"
config[:linter_version] = rubocop_version
Bundler.ui.info "RuboCop enabled in config"
templates.merge!("rubocop.yml.tt" => ".rubocop.yml")
when "standard"
config[:linter_version] = standard_version
Bundler.ui.info "Standard enabled in config"
templates.merge!("standard.yml.tt" => ".standard.yml")
end
templates.merge!("exe/newgem.tt" => "exe/#{name}") if config[:exe]
if extension == "c"
templates.merge!(
"ext/newgem/extconf-c.rb.tt" => "ext/#{name}/extconf.rb",
"ext/newgem/newgem.h.tt" => "ext/#{name}/#{underscored_name}.h",
"ext/newgem/newgem.c.tt" => "ext/#{name}/#{underscored_name}.c"
)
end
if extension == "rust"
templates.merge!(
"Cargo.toml.tt" => "Cargo.toml",
"ext/newgem/Cargo.toml.tt" => "ext/#{name}/Cargo.toml",
"ext/newgem/extconf-rust.rb.tt" => "ext/#{name}/extconf.rb",
"ext/newgem/src/lib.rs.tt" => "ext/#{name}/src/lib.rs",
)
end
if target.exist? && !target.directory?
Bundler.ui.error "Couldn't create a new gem named `#{gem_name}` because there's an existing file named `#{gem_name}`."
exit Bundler::BundlerError.all_errors[Bundler::GenericSystemCallError]
end
if use_git
Bundler.ui.info "Initializing git repo in #{target}"
require "shellwords"
`git init #{target.to_s.shellescape}`
config[:git_default_branch] = File.read("#{target}/.git/HEAD").split("/").last.chomp
end
templates.each do |src, dst|
destination = target.join(dst)
thor.template("newgem/#{src}", destination, config)
end
executables.each do |file|
path = target.join(file)
executable = (path.stat.mode | 0o111)
path.chmod(executable)
end
if use_git
Dir.chdir(target) do
`git add .`
end
end
# Open gemspec in editor
open_editor(options["edit"], target.join("#{name}.gemspec")) if options[:edit]
Bundler.ui.info "Gem '#{name}' was successfully created. " \
"For more information on making a RubyGem visit https://bundler.io/guides/creating_gem.html"
end
private
def resolve_name(name)
SharedHelpers.pwd.join(name).basename.to_s
end
def ask_and_set(key, header, message)
choice = options[key]
choice = Bundler.settings["gem.#{key}"] if choice.nil?
if choice.nil?
Bundler.ui.confirm header
choice = Bundler.ui.yes? "#{message} y/(n):"
Bundler.settings.set_global("gem.#{key}", choice)
end
choice
end
def validate_ext_name
return unless gem_name.index("-")
Bundler.ui.error "You have specified a gem name which does not conform to the \n" \
"naming guidelines for C extensions. For more information, \n" \
"see the 'Extension Naming' section at the following URL:\n" \
"https://guides.rubygems.org/gems-with-extensions/\n"
exit 1
end
def ask_and_set_test_framework
test_framework = options[:test] || Bundler.settings["gem.test"]
if test_framework.to_s.empty?
Bundler.ui.confirm "Do you want to generate tests with your gem?"
Bundler.ui.info hint_text("test")
result = Bundler.ui.ask "Enter a test framework. rspec/minitest/test-unit/(none):"
if /rspec|minitest|test-unit/.match?(result)
test_framework = result
else
test_framework = false
end
end
if Bundler.settings["gem.test"].nil?
Bundler.settings.set_global("gem.test", test_framework)
end
if options[:test] == Bundler.settings["gem.test"]
Bundler.ui.info "#{options[:test]} is already configured, ignoring --test flag."
end
test_framework
end
def hint_text(setting)
if Bundler.settings["gem.#{setting}"] == false
"Your choice will only be applied to this gem."
else
"Future `bundle gem` calls will use your choice. " \
"This setting can be changed anytime with `bundle config gem.#{setting}`."
end
end
def ask_and_set_ci
ci_template = options[:ci] || Bundler.settings["gem.ci"]
if ci_template.to_s.empty?
Bundler.ui.confirm "Do you want to set up continuous integration for your gem? " \
"Supported services:\n" \
"* CircleCI: https://circleci.com/\n" \
"* GitHub Actions: https://github.com/features/actions\n" \
"* GitLab CI: https://docs.gitlab.com/ee/ci/\n" \
"\n"
Bundler.ui.info hint_text("ci")
result = Bundler.ui.ask "Enter a CI service. github/gitlab/circle/(none):"
if /github|gitlab|circle/.match?(result)
ci_template = result
else
ci_template = false
end
end
if Bundler.settings["gem.ci"].nil?
Bundler.settings.set_global("gem.ci", ci_template)
end
if options[:ci] == Bundler.settings["gem.ci"]
Bundler.ui.info "#{options[:ci]} is already configured, ignoring --ci flag."
end
ci_template
end
def ask_and_set_linter
linter_template = options[:linter] || Bundler.settings["gem.linter"]
linter_template = deprecated_rubocop_option if linter_template.nil?
if linter_template.to_s.empty?
Bundler.ui.confirm "Do you want to add a code linter and formatter to your gem? " \
"Supported Linters:\n" \
"* RuboCop: https://rubocop.org\n" \
"* Standard: https://github.com/testdouble/standard\n" \
"\n"
Bundler.ui.info hint_text("linter")
result = Bundler.ui.ask "Enter a linter. rubocop/standard/(none):"
if /rubocop|standard/.match?(result)
linter_template = result
else
linter_template = false
end
end
if Bundler.settings["gem.linter"].nil?
Bundler.settings.set_global("gem.linter", linter_template)
end
# Once gem.linter safely set, unset the deprecated gem.rubocop
unless Bundler.settings["gem.rubocop"].nil?
Bundler.settings.set_global("gem.rubocop", nil)
end
if options[:linter] == Bundler.settings["gem.linter"]
Bundler.ui.info "#{options[:linter]} is already configured, ignoring --linter flag."
end
linter_template
end
def deprecated_rubocop_option
if !options[:rubocop].nil?
if options[:rubocop]
Bundler::SharedHelpers.major_deprecation 2, "--rubocop is deprecated, use --linter=rubocop"
"rubocop"
else
Bundler::SharedHelpers.major_deprecation 2, "--no-rubocop is deprecated, use --linter"
false
end
elsif !Bundler.settings["gem.rubocop"].nil?
Bundler::SharedHelpers.major_deprecation 2,
"config gem.rubocop is deprecated; we've updated your config to use gem.linter instead"
Bundler.settings["gem.rubocop"] ? "rubocop" : false
end
end
def bundler_dependency_version
v = Gem::Version.new(Bundler::VERSION)
req = v.segments[0..1]
req << "a" if v.prerelease?
req.join(".")
end
def ensure_safe_gem_name(name, constant_array)
if /^\d/.match?(name)
Bundler.ui.error "Invalid gem name #{name} Please give a name which does not start with numbers."
exit 1
end
constant_name = constant_array.join("::")
existing_constant = constant_array.inject(Object) do |c, s|
defined = begin
c.const_defined?(s)
rescue NameError
Bundler.ui.error "Invalid gem name #{name} -- `#{constant_name}` is an invalid constant name"
exit 1
end
(defined && c.const_get(s)) || break
end
return unless existing_constant
Bundler.ui.error "Invalid gem name #{name} constant #{constant_name} is already in use. Please choose another gem name."
exit 1
end
def open_editor(editor, file)
thor.run(%(#{editor} "#{file}"))
end
def rust_builder_required_rubygems_version
"3.3.11"
end
def required_ruby_version
"2.6.0"
end
def rubocop_version
"1.21"
end
def standard_version
"1.3"
end
# TODO: remove at next minor release
def travis_removal_info
if options[:ci] == "travis"
Bundler.ui.error "Support for Travis CI was removed from gem skeleton generator."
exit 1
end
if Bundler.settings["gem.ci"] == "travis"
Bundler.ui.error "Support for Travis CI was removed from gem skeleton generator, but it is present in bundle config. Please configure another provider using `bundle config set gem.ci SERVICE` (where SERVICE is one of github/gitlab/circle) or unset configuration using `bundle config unset gem.ci`."
exit 1
end
end
def validate_rust_builder_rubygems_version
if Gem::Version.new(rust_builder_required_rubygems_version) > Gem.rubygems_version
Bundler.ui.error "Your RubyGems version (#{Gem.rubygems_version}) is too old to build Rust extension. Please update your RubyGems using `gem update --system` or any other way and try again."
exit 1
end
end
end
end

View File

@@ -1,94 +0,0 @@
# frozen_string_literal: true
module Bundler
class CLI::Info
attr_reader :gem_name, :options
def initialize(options, gem_name)
@options = options
@gem_name = gem_name
end
def run
Bundler.ui.silence do
Bundler.definition.validate_runtime!
Bundler.load.lock
end
spec = spec_for_gem(gem_name)
if spec
return print_gem_path(spec) if @options[:path]
return print_gem_version(spec) if @options[:version]
print_gem_info(spec)
end
end
private
def spec_for_gem(gem_name)
spec = Bundler.definition.specs.find {|s| s.name == gem_name }
spec || default_gem_spec(gem_name) || Bundler::CLI::Common.select_spec(gem_name, :regex_match)
end
def default_gem_spec(gem_name)
return unless Gem::Specification.respond_to?(:find_all_by_name)
gem_spec = Gem::Specification.find_all_by_name(gem_name).last
return gem_spec if gem_spec&.default_gem?
end
def spec_not_found(gem_name)
raise GemNotFound, Bundler::CLI::Common.gem_not_found_message(gem_name, Bundler.definition.dependencies)
end
def print_gem_version(spec)
Bundler.ui.info spec.version.to_s
end
def print_gem_path(spec)
name = spec.name
if name == "bundler"
path = File.expand_path("../../..", __dir__)
else
path = spec.full_gem_path
if spec.deleted_gem?
return Bundler.ui.warn "The gem #{name} has been deleted. It was installed at: #{path}"
end
end
Bundler.ui.info path
end
def print_gem_info(spec)
metadata = spec.metadata
name = spec.name
gem_info = String.new
gem_info << " * #{name} (#{spec.version}#{spec.git_version})\n"
gem_info << "\tSummary: #{spec.summary}\n" if spec.summary
gem_info << "\tHomepage: #{spec.homepage}\n" if spec.homepage
gem_info << "\tDocumentation: #{metadata["documentation_uri"]}\n" if metadata.key?("documentation_uri")
gem_info << "\tSource Code: #{metadata["source_code_uri"]}\n" if metadata.key?("source_code_uri")
gem_info << "\tFunding: #{metadata["funding_uri"]}\n" if metadata.key?("funding_uri")
gem_info << "\tWiki: #{metadata["wiki_uri"]}\n" if metadata.key?("wiki_uri")
gem_info << "\tChangelog: #{metadata["changelog_uri"]}\n" if metadata.key?("changelog_uri")
gem_info << "\tBug Tracker: #{metadata["bug_tracker_uri"]}\n" if metadata.key?("bug_tracker_uri")
gem_info << "\tMailing List: #{metadata["mailing_list_uri"]}\n" if metadata.key?("mailing_list_uri")
gem_info << "\tPath: #{spec.full_gem_path}\n"
gem_info << "\tDefault Gem: yes\n" if spec.respond_to?(:default_gem?) && spec.default_gem?
gem_info << "\tReverse Dependencies: \n\t\t#{gem_dependencies.join("\n\t\t")}" if gem_dependencies.any?
if name != "bundler" && spec.deleted_gem?
return Bundler.ui.warn "The gem #{name} has been deleted. Gemspec information is still available though:\n#{gem_info}"
end
Bundler.ui.info gem_info
end
def gem_dependencies
@gem_dependencies ||= Bundler.definition.specs.map do |spec|
dependency = spec.dependencies.find {|dep| dep.name == gem_name }
next unless dependency
"#{spec.name} (#{spec.version}) depends on #{gem_name} (#{dependency.requirements_list.join(", ")})"
end.compact.sort
end
end
end

View File

@@ -1,51 +0,0 @@
# frozen_string_literal: true
module Bundler
class CLI::Init
attr_reader :options
def initialize(options)
@options = options
end
def run
if File.exist?(gemfile)
Bundler.ui.error "#{gemfile} already exists at #{File.expand_path(gemfile)}"
exit 1
end
unless File.writable?(Dir.pwd)
Bundler.ui.error "Can not create #{gemfile} as the current directory is not writable."
exit 1
end
if options[:gemspec]
gemspec = File.expand_path(options[:gemspec])
unless File.exist?(gemspec)
Bundler.ui.error "Gem specification #{gemspec} doesn't exist"
exit 1
end
spec = Bundler.load_gemspec_uncached(gemspec)
File.open(gemfile, "wb") do |file|
file << "# Generated from #{gemspec}\n"
file << spec.to_gemfile
end
else
File.open(File.expand_path("../templates/Gemfile", __dir__), "r") do |template|
File.open(gemfile, "wb") do |destination|
IO.copy_stream(template, destination)
end
end
end
puts "Writing new #{gemfile} to #{SharedHelpers.pwd}/#{gemfile}"
end
private
def gemfile
@gemfile ||= options[:gemfile] || Bundler.preferred_gemfile_name
end
end
end

View File

@@ -1,60 +0,0 @@
# frozen_string_literal: true
module Bundler
class CLI::Inject
attr_reader :options, :name, :version, :group, :source, :gems
def initialize(options, name, version)
@options = options
@name = name
@version = version || last_version_number
@group = options[:group].split(",") unless options[:group].nil?
@source = options[:source]
@gems = []
end
def run
# The required arguments allow Thor to give useful feedback when the arguments
# are incorrect. This adds those first two arguments onto the list as a whole.
gems.unshift(source).unshift(group).unshift(version).unshift(name)
# Build an array of Dependency objects out of the arguments
deps = []
# when `inject` support addition of more than one gem, then this loop will
# help. Currently this loop is running once.
gems.each_slice(4) do |gem_name, gem_version, gem_group, gem_source|
ops = Gem::Requirement::OPS.map {|key, _val| key }
has_op = ops.any? {|op| gem_version.start_with? op }
gem_version = "~> #{gem_version}" unless has_op
deps << Bundler::Dependency.new(gem_name, gem_version, "group" => gem_group, "source" => gem_source)
end
added = Injector.inject(deps, options)
if added.any?
Bundler.ui.confirm "Added to Gemfile:"
Bundler.ui.confirm(added.map do |d|
name = "'#{d.name}'"
requirement = ", '#{d.requirement}'"
group = ", :group => #{d.groups.inspect}" if d.groups != Array(:default)
source = ", :source => '#{d.source}'" unless d.source.nil?
%(gem #{name}#{requirement}#{group}#{source})
end.join("\n"))
else
Bundler.ui.confirm "All gems were already present in the Gemfile"
end
end
private
def last_version_number
definition = Bundler.definition(true)
definition.resolve_remotely!
specs = definition.index[name].sort_by(&:version)
unless options[:pre]
specs.delete_if {|b| b.respond_to?(:version) && b.version.prerelease? }
end
spec = specs.last
spec.version.to_s
end
end
end

View File

@@ -1,189 +0,0 @@
# frozen_string_literal: true
module Bundler
class CLI::Install
attr_reader :options
def initialize(options)
@options = options
end
def run
Bundler.ui.level = "warn" if options[:quiet]
warn_if_root
Bundler.self_manager.install_locked_bundler_and_restart_with_it_if_needed
Bundler::SharedHelpers.set_env "RB_USER_INSTALL", "1" if Bundler::FREEBSD
# Disable color in deployment mode
Bundler.ui.shell = Thor::Shell::Basic.new if options[:deployment]
check_for_options_conflicts
check_trust_policy
if options[:deployment] || options[:frozen] || Bundler.frozen_bundle?
unless Bundler.default_lockfile.exist?
flag = "--deployment flag" if options[:deployment]
flag ||= "--frozen flag" if options[:frozen]
flag ||= "deployment setting"
raise ProductionError, "The #{flag} requires a #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)}. Please make " \
"sure you have checked your #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)} into version control " \
"before deploying."
end
options[:local] = true if Bundler.app_cache.exist?
Bundler.settings.set_command_option :deployment, true if options[:deployment]
Bundler.settings.set_command_option :frozen, true if options[:frozen]
end
# When install is called with --no-deployment, disable deployment mode
if options[:deployment] == false
Bundler.settings.set_command_option :frozen, nil
options[:system] = true
end
normalize_settings
Bundler::Fetcher.disable_endpoint = options["full-index"]
if options["binstubs"]
Bundler::SharedHelpers.major_deprecation 2,
"The --binstubs option will be removed in favor of `bundle binstubs --all`"
end
Plugin.gemfile_install(Bundler.default_gemfile) if Bundler.feature_flag.plugins?
definition = Bundler.definition
definition.validate_runtime!
installer = Installer.install(Bundler.root, definition, options)
Bundler.settings.temporary(:cache_all_platforms => options[:local] ? false : Bundler.settings[:cache_all_platforms]) do
Bundler.load.cache(nil, options[:local]) if Bundler.app_cache.exist? && !options["no-cache"] && !Bundler.frozen_bundle?
end
Bundler.ui.confirm "Bundle complete! #{dependencies_count_for(definition)}, #{gems_installed_for(definition)}."
Bundler::CLI::Common.output_without_groups_message(:install)
if Bundler.use_system_gems?
Bundler.ui.confirm "Use `bundle info [gemname]` to see where a bundled gem is installed."
else
relative_path = Bundler.configured_bundle_path.base_path_relative_to_pwd
Bundler.ui.confirm "Bundled gems are installed into `#{relative_path}`"
end
Bundler::CLI::Common.output_post_install_messages installer.post_install_messages
warn_ambiguous_gems
if CLI::Common.clean_after_install?
require_relative "clean"
Bundler::CLI::Clean.new(options).run
end
Bundler::CLI::Common.output_fund_metadata_summary
rescue Gem::InvalidSpecificationException
Bundler.ui.warn "You have one or more invalid gemspecs that need to be fixed."
raise
end
private
def warn_if_root
return if Bundler.settings[:silence_root_warning] || Gem.win_platform? || !Process.uid.zero?
Bundler.ui.warn "Don't run Bundler as root. Installing your bundle as root " \
"will break this application for all non-root users on this machine.", :wrap => true
end
def dependencies_count_for(definition)
count = definition.dependencies.count
"#{count} Gemfile #{count == 1 ? "dependency" : "dependencies"}"
end
def gems_installed_for(definition)
count = definition.specs.count
"#{count} #{count == 1 ? "gem" : "gems"} now installed"
end
def check_for_group_conflicts_in_cli_options
conflicting_groups = Array(options[:without]) & Array(options[:with])
return if conflicting_groups.empty?
raise InvalidOption, "You can't list a group in both with and without." \
" The offending groups are: #{conflicting_groups.join(", ")}."
end
def check_for_options_conflicts
if (options[:path] || options[:deployment]) && options[:system]
error_message = String.new
error_message << "You have specified both --path as well as --system. Please choose only one option.\n" if options[:path]
error_message << "You have specified both --deployment as well as --system. Please choose only one option.\n" if options[:deployment]
raise InvalidOption.new(error_message)
end
end
def check_trust_policy
trust_policy = options["trust-policy"]
unless Bundler.rubygems.security_policies.keys.unshift(nil).include?(trust_policy)
raise InvalidOption, "RubyGems doesn't know about trust policy '#{trust_policy}'. " \
"The known policies are: #{Bundler.rubygems.security_policies.keys.join(", ")}."
end
Bundler.settings.set_command_option_if_given :"trust-policy", trust_policy
end
def normalize_groups
check_for_group_conflicts_in_cli_options
# need to nil them out first to get around validation for backwards compatibility
Bundler.settings.set_command_option :without, nil
Bundler.settings.set_command_option :with, nil
Bundler.settings.set_command_option :without, options[:without]
Bundler.settings.set_command_option :with, options[:with]
end
def normalize_settings
Bundler.settings.set_command_option :path, nil if options[:system]
Bundler.settings.set_command_option_if_given :path, options[:path]
if options["standalone"] && Bundler.settings[:path].nil? && !options["local"]
Bundler.settings.temporary(:path_relative_to_cwd => false) do
Bundler.settings.set_command_option :path, "bundle"
end
end
bin_option = options["binstubs"]
bin_option = nil if bin_option&.empty?
Bundler.settings.set_command_option :bin, bin_option if options["binstubs"]
Bundler.settings.set_command_option_if_given :shebang, options["shebang"]
Bundler.settings.set_command_option_if_given :jobs, options["jobs"]
Bundler.settings.set_command_option_if_given :no_prune, options["no-prune"]
Bundler.settings.set_command_option_if_given :no_install, options["no-install"]
Bundler.settings.set_command_option_if_given :clean, options["clean"]
normalize_groups if options[:without] || options[:with]
options[:force] = options[:redownload]
end
def warn_ambiguous_gems
# TODO: remove this when we drop Bundler 1.x support
Installer.ambiguous_gems.to_a.each do |name, installed_from_uri, *also_found_in_uris|
Bundler.ui.warn "Warning: the gem '#{name}' was found in multiple sources."
Bundler.ui.warn "Installed from: #{installed_from_uri}"
Bundler.ui.warn "Also found in:"
also_found_in_uris.each {|uri| Bundler.ui.warn " * #{uri}" }
Bundler.ui.warn "You should add a source requirement to restrict this gem to your preferred source."
Bundler.ui.warn "For example:"
Bundler.ui.warn " gem '#{name}', :source => '#{installed_from_uri}'"
Bundler.ui.warn "Then uninstall the gem '#{name}' (or delete all bundled gems) and then install again."
end
end
end
end

View File

@@ -1,41 +0,0 @@
# frozen_string_literal: true
require "rbconfig"
module Bundler
class CLI::Issue
def run
Bundler.ui.info <<-EOS.gsub(/^ {8}/, "")
Did you find an issue with Bundler? Before filing a new issue,
be sure to check out these resources:
1. Check out our troubleshooting guide for quick fixes to common issues:
https://github.com/rubygems/rubygems/blob/master/bundler/doc/TROUBLESHOOTING.md
2. Instructions for common Bundler uses can be found on the documentation
site: https://bundler.io/
3. Information about each Bundler command can be found in the Bundler
man pages: https://bundler.io/man/bundle.1.html
Hopefully the troubleshooting steps above resolved your problem! If things
still aren't working the way you expect them to, please let us know so
that we can diagnose and help fix the problem you're having, by filling
in the new issue form located at
https://github.com/rubygems/rubygems/issues/new?labels=Bundler&template=bundler-related-issue.md,
and copy and pasting the information below.
EOS
Bundler.ui.info Bundler::Env.report
Bundler.ui.info "\n## Bundle Doctor"
doctor
end
def doctor
require_relative "doctor"
Bundler::CLI::Doctor.new({}).run
end
end
end

View File

@@ -1,66 +0,0 @@
# frozen_string_literal: true
module Bundler
class CLI::List
def initialize(options)
@options = options
@without_group = options["without-group"].map(&:to_sym)
@only_group = options["only-group"].map(&:to_sym)
end
def run
raise InvalidOption, "The `--only-group` and `--without-group` options cannot be used together" if @only_group.any? && @without_group.any?
raise InvalidOption, "The `--name-only` and `--paths` options cannot be used together" if @options["name-only"] && @options[:paths]
specs = if @only_group.any? || @without_group.any?
filtered_specs_by_groups
else
begin
Bundler.load.specs
rescue GemNotFound => e
Bundler.ui.error e.message
Bundler.ui.warn "Install missing gems with `bundle install`."
exit 1
end
end.reject {|s| s.name == "bundler" }.sort_by(&:name)
return Bundler.ui.info "No gems in the Gemfile" if specs.empty?
return specs.each {|s| Bundler.ui.info s.name } if @options["name-only"]
return specs.each {|s| Bundler.ui.info s.full_gem_path } if @options["paths"]
Bundler.ui.info "Gems included by the bundle:"
specs.each {|s| Bundler.ui.info " * #{s.name} (#{s.version}#{s.git_version})" }
Bundler.ui.info "Use `bundle info` to print more detailed information about a gem"
end
private
def verify_group_exists(groups)
(@without_group + @only_group).each do |group|
raise InvalidOption, "`#{group}` group could not be found." unless groups.include?(group)
end
end
def filtered_specs_by_groups
definition = Bundler.definition
groups = definition.groups
verify_group_exists(groups)
show_groups =
if @without_group.any?
groups.reject {|g| @without_group.include?(g) }
elsif @only_group.any?
groups.select {|g| @only_group.include?(g) }
else
groups
end.map(&:to_sym)
definition.specs_for(show_groups)
end
end
end

View File

@@ -1,70 +0,0 @@
# frozen_string_literal: true
module Bundler
class CLI::Lock
attr_reader :options
def initialize(options)
@options = options
end
def run
unless Bundler.default_gemfile
Bundler.ui.error "Unable to find a Gemfile to lock"
exit 1
end
print = options[:print]
previous_ui_level = Bundler.ui.level
Bundler.ui.level = "silent" if print
Bundler::Fetcher.disable_endpoint = options["full-index"]
update = options[:update]
conservative = options[:conservative]
bundler = options[:bundler]
if update.is_a?(Array) # unlocking specific gems
Bundler::CLI::Common.ensure_all_gems_in_lockfile!(update)
update = { :gems => update, :conservative => conservative }
elsif update && conservative
update = { :conservative => conservative }
elsif update && bundler
update = { :bundler => bundler }
end
definition = Bundler.definition(update)
Bundler::CLI::Common.configure_gem_version_promoter(Bundler.definition, options) if options[:update]
options["remove-platform"].each do |platform|
definition.remove_platform(platform)
end
options["add-platform"].each do |platform_string|
platform = Gem::Platform.new(platform_string)
if platform.to_s == "unknown"
Bundler.ui.warn "The platform `#{platform_string}` is unknown to RubyGems " \
"and adding it will likely lead to resolution errors"
end
definition.add_platform(platform)
end
if definition.platforms.empty?
raise InvalidOption, "Removing all platforms from the bundle is not allowed"
end
definition.resolve_remotely! unless options[:local]
if print
puts definition.to_lock
else
file = options[:lockfile]
file = file ? File.expand_path(file) : Bundler.default_lockfile
puts "Writing lockfile to #{file}"
definition.lock(file)
end
Bundler.ui.level = previous_ui_level
end
end
end

View File

@@ -1,31 +0,0 @@
# frozen_string_literal: true
module Bundler
class CLI::Open
attr_reader :options, :name, :path
def initialize(options, name)
@options = options
@name = name
@path = options[:path] unless options[:path].nil?
end
def run
raise InvalidOption, "Cannot specify `--path` option without a value" if !@path.nil? && @path.empty?
editor = [ENV["BUNDLER_EDITOR"], ENV["VISUAL"], ENV["EDITOR"]].find {|e| !e.nil? && !e.empty? }
return Bundler.ui.info("To open a bundled gem, set $EDITOR or $BUNDLER_EDITOR") unless editor
return unless spec = Bundler::CLI::Common.select_spec(name, :regex_match)
if spec.default_gem?
Bundler.ui.info "Unable to open #{name} because it's a default gem, so the directory it would normally be installed to does not exist."
else
root_path = spec.full_gem_path
Dir.chdir(root_path) do
require "shellwords"
command = Shellwords.split(editor) << File.join([root_path, path].compact)
Bundler.with_original_env do
system(*command)
end || Bundler.ui.info("Could not run '#{command.join(" ")}'")
end
end
end
end
end

View File

@@ -1,297 +0,0 @@
# frozen_string_literal: true
module Bundler
class CLI::Outdated
attr_reader :options, :gems, :options_include_groups, :filter_options_patch, :sources, :strict
attr_accessor :outdated_gems
def initialize(options, gems)
@options = options
@gems = gems
@sources = Array(options[:source])
@filter_options_patch = options.keys & %w[filter-major filter-minor filter-patch]
@outdated_gems = []
@options_include_groups = [:group, :groups].any? do |v|
options.keys.include?(v.to_s)
end
# the patch level options imply strict is also true. It wouldn't make
# sense otherwise.
@strict = options["filter-strict"] || Bundler::CLI::Common.patch_level_options(options).any?
end
def run
check_for_deployment_mode!
gems.each do |gem_name|
Bundler::CLI::Common.select_spec(gem_name)
end
Bundler.definition.validate_runtime!
current_specs = Bundler.ui.silence { Bundler.definition.resolve }
current_dependencies = Bundler.ui.silence do
Bundler.load.dependencies.map {|dep| [dep.name, dep] }.to_h
end
definition = if gems.empty? && sources.empty?
# We're doing a full update
Bundler.definition(true)
else
Bundler.definition(:gems => gems, :sources => sources)
end
Bundler::CLI::Common.configure_gem_version_promoter(
Bundler.definition,
options.merge(:strict => @strict)
)
definition_resolution = proc do
options[:local] ? definition.resolve_with_cache! : definition.resolve_remotely!
end
if options[:parseable]
Bundler.ui.silence(&definition_resolution)
else
definition_resolution.call
end
Bundler.ui.info ""
# Loop through the current specs
gemfile_specs, dependency_specs = current_specs.partition do |spec|
current_dependencies.key? spec.name
end
specs = if options["only-explicit"]
gemfile_specs
else
gemfile_specs + dependency_specs
end
specs.sort_by(&:name).uniq(&:name).each do |current_spec|
next unless gems.empty? || gems.include?(current_spec.name)
active_spec = retrieve_active_spec(definition, current_spec)
next unless active_spec
next unless filter_options_patch.empty? || update_present_via_semver_portions(current_spec, active_spec, options)
gem_outdated = Gem::Version.new(active_spec.version) > Gem::Version.new(current_spec.version)
next unless gem_outdated || (current_spec.git_version != active_spec.git_version)
dependency = current_dependencies[current_spec.name]
groups = ""
if dependency && !options[:parseable]
groups = dependency.groups.join(", ")
end
outdated_gems << {
:active_spec => active_spec,
:current_spec => current_spec,
:dependency => dependency,
:groups => groups,
}
end
if outdated_gems.empty?
unless options[:parseable]
Bundler.ui.info(nothing_outdated_message)
end
else
if options_include_groups
relevant_outdated_gems = outdated_gems.group_by {|g| g[:groups] }.sort.flat_map do |groups, gems|
contains_group = groups.split(", ").include?(options[:group])
next unless options[:groups] || contains_group
gems
end.compact
if options[:parseable]
print_gems(relevant_outdated_gems)
else
print_gems_table(relevant_outdated_gems)
end
elsif options[:parseable]
print_gems(outdated_gems)
else
print_gems_table(outdated_gems)
end
exit 1
end
end
private
def loaded_from_for(spec)
return unless spec.respond_to?(:loaded_from)
spec.loaded_from
end
def groups_text(group_text, groups)
"#{group_text}#{groups.split(",").size > 1 ? "s" : ""} \"#{groups}\""
end
def nothing_outdated_message
if filter_options_patch.any?
display = filter_options_patch.map do |o|
o.sub("filter-", "")
end.join(" or ")
"No #{display} updates to display.\n"
else
"Bundle up to date!\n"
end
end
def retrieve_active_spec(definition, current_spec)
active_spec = definition.resolve.find_by_name_and_platform(current_spec.name, current_spec.platform)
return unless active_spec
return active_spec if strict
active_specs = active_spec.source.specs.search(current_spec.name).select {|spec| spec.match_platform(current_spec.platform) }.sort_by(&:version)
if !current_spec.version.prerelease? && !options[:pre] && active_specs.size > 1
active_specs.delete_if {|b| b.respond_to?(:version) && b.version.prerelease? }
end
active_specs.last
end
def print_gems(gems_list)
gems_list.each do |gem|
print_gem(
gem[:current_spec],
gem[:active_spec],
gem[:dependency],
gem[:groups],
)
end
end
def print_gems_table(gems_list)
data = gems_list.map do |gem|
gem_column_for(
gem[:current_spec],
gem[:active_spec],
gem[:dependency],
gem[:groups],
)
end
print_indented([table_header] + data)
end
def print_gem(current_spec, active_spec, dependency, groups)
spec_version = "#{active_spec.version}#{active_spec.git_version}"
if Bundler.ui.debug?
loaded_from = loaded_from_for(active_spec)
spec_version += " (from #{loaded_from})" if loaded_from
end
current_version = "#{current_spec.version}#{current_spec.git_version}"
if dependency&.specific?
dependency_version = %(, requested #{dependency.requirement})
end
spec_outdated_info = "#{active_spec.name} (newest #{spec_version}, " \
"installed #{current_version}#{dependency_version})"
output_message = if options[:parseable]
spec_outdated_info.to_s
elsif options_include_groups || groups.empty?
" * #{spec_outdated_info}"
else
" * #{spec_outdated_info} in #{groups_text("group", groups)}"
end
Bundler.ui.info output_message.rstrip
end
def gem_column_for(current_spec, active_spec, dependency, groups)
current_version = "#{current_spec.version}#{current_spec.git_version}"
spec_version = "#{active_spec.version}#{active_spec.git_version}"
dependency = dependency.requirement if dependency
ret_val = [active_spec.name, current_version, spec_version, dependency.to_s, groups.to_s]
ret_val << loaded_from_for(active_spec).to_s if Bundler.ui.debug?
ret_val
end
def check_for_deployment_mode!
return unless Bundler.frozen_bundle?
suggested_command = if Bundler.settings.locations("frozen").keys.&([:global, :local]).any?
"bundle config unset frozen"
elsif Bundler.settings.locations("deployment").keys.&([:global, :local]).any?
"bundle config unset deployment"
end
raise ProductionError, "You are trying to check outdated gems in " \
"deployment mode. Run `bundle outdated` elsewhere.\n" \
"\nIf this is a development machine, remove the " \
"#{Bundler.default_gemfile} freeze" \
"\nby running `#{suggested_command}`."
end
def update_present_via_semver_portions(current_spec, active_spec, options)
current_major = current_spec.version.segments.first
active_major = active_spec.version.segments.first
update_present = false
update_present = active_major > current_major if options["filter-major"]
if !update_present && (options["filter-minor"] || options["filter-patch"]) && current_major == active_major
current_minor = get_version_semver_portion_value(current_spec, 1)
active_minor = get_version_semver_portion_value(active_spec, 1)
update_present = active_minor > current_minor if options["filter-minor"]
if !update_present && options["filter-patch"] && current_minor == active_minor
current_patch = get_version_semver_portion_value(current_spec, 2)
active_patch = get_version_semver_portion_value(active_spec, 2)
update_present = active_patch > current_patch
end
end
update_present
end
def get_version_semver_portion_value(spec, version_portion_index)
version_section = spec.version.segments[version_portion_index, 1]
version_section.to_a[0].to_i
end
def print_indented(matrix)
header = matrix[0]
data = matrix[1..-1]
column_sizes = Array.new(header.size) do |index|
matrix.max_by {|row| row[index].length }[index].length
end
Bundler.ui.info justify(header, column_sizes)
data.sort_by! {|row| row[0] }
data.each do |row|
Bundler.ui.info justify(row, column_sizes)
end
end
def table_header
header = ["Gem", "Current", "Latest", "Requested", "Groups"]
header << "Path" if Bundler.ui.debug?
header
end
def justify(row, sizes)
row.each_with_index.map do |element, index|
element.ljust(sizes[index])
end.join(" ").strip + "\n"
end
end
end

View File

@@ -1,48 +0,0 @@
# frozen_string_literal: true
module Bundler
class CLI::Platform
attr_reader :options
def initialize(options)
@options = options
end
def run
ruby_version = if Bundler.locked_gems
Bundler.locked_gems.ruby_version&.gsub(/p\d+\Z/, "")
else
Bundler.definition.ruby_version&.single_version_string
end
output = []
if options[:ruby]
if ruby_version
output << ruby_version
else
output << "No ruby version specified"
end
else
platforms = Bundler.definition.platforms.map {|p| "* #{p}" }
output << "Your platform is: #{Gem::Platform.local}"
output << "Your app has gems that work on these platforms:\n#{platforms.join("\n")}"
if ruby_version
output << "Your Gemfile specifies a Ruby version requirement:\n* #{ruby_version}"
begin
Bundler.definition.validate_runtime!
output << "Your current platform satisfies the Ruby version requirement."
rescue RubyVersionMismatch => e
output << e.message
end
else
output << "Your Gemfile does not specify a Ruby version requirement."
end
end
Bundler.ui.info output.join("\n\n")
end
end
end

View File

@@ -1,41 +0,0 @@
# frozen_string_literal: true
require_relative "../vendored_thor"
module Bundler
class CLI::Plugin < Thor
desc "install PLUGINS", "Install the plugin from the source"
long_desc <<-D
Install plugins either from the rubygems source provided (with --source option) or from a git source provided with --git (for remote repos) or --local_git (for local repos). If no sources are provided, it uses Gem.sources
D
method_option "source", :type => :string, :default => nil, :banner =>
"URL of the RubyGems source to fetch the plugin from"
method_option "version", :type => :string, :default => nil, :banner =>
"The version of the plugin to fetch"
method_option "git", :type => :string, :default => nil, :banner =>
"URL of the git repo to fetch from"
method_option "local_git", :type => :string, :default => nil, :banner =>
"Path of the local git repo to fetch from"
method_option "branch", :type => :string, :default => nil, :banner =>
"The git branch to checkout"
method_option "ref", :type => :string, :default => nil, :banner =>
"The git revision to check out"
def install(*plugins)
Bundler::Plugin.install(plugins, options)
end
desc "uninstall PLUGINS", "Uninstall the plugins"
long_desc <<-D
Uninstall given list of plugins. To uninstall all the plugins, use -all option.
D
method_option "all", :type => :boolean, :default => nil, :banner =>
"Uninstall all the installed plugins. If no plugin is installed, then it does nothing."
def uninstall(*plugins)
Bundler::Plugin.uninstall(plugins, options)
end
desc "list", "List the installed plugins and available commands"
def list
Bundler::Plugin.list
end
end
end

View File

@@ -1,52 +0,0 @@
# frozen_string_literal: true
module Bundler
class CLI::Pristine
def initialize(gems)
@gems = gems
end
def run
CLI::Common.ensure_all_gems_in_lockfile!(@gems)
definition = Bundler.definition
definition.validate_runtime!
installer = Bundler::Installer.new(Bundler.root, definition)
Bundler.load.specs.each do |spec|
next if spec.name == "bundler" # Source::Rubygems doesn't install bundler
next if !@gems.empty? && !@gems.include?(spec.name)
gem_name = "#{spec.name} (#{spec.version}#{spec.git_version})"
gem_name += " (#{spec.platform})" if !spec.platform.nil? && spec.platform != Gem::Platform::RUBY
case source = spec.source
when Source::Rubygems
cached_gem = spec.cache_file
unless File.exist?(cached_gem)
Bundler.ui.error("Failed to pristine #{gem_name}. Cached gem #{cached_gem} does not exist.")
next
end
FileUtils.rm_rf spec.full_gem_path
when Source::Git
if source.local?
Bundler.ui.warn("Cannot pristine #{gem_name}. Gem is locally overridden.")
next
end
source.remote!
if extension_cache_path = source.extension_cache_path(spec)
FileUtils.rm_rf extension_cache_path
end
FileUtils.rm_rf spec.extension_dir
FileUtils.rm_rf spec.full_gem_path
else
Bundler.ui.warn("Cannot pristine #{gem_name}. Gem is sourced from local path.")
next
end
Bundler::GemInstaller.new(spec, installer, false, 0, true).install_from_spec
end
end
end
end

View File

@@ -1,17 +0,0 @@
# frozen_string_literal: true
module Bundler
class CLI::Remove
def initialize(gems, options)
@gems = gems
@options = options
end
def run
raise InvalidOption, "Please specify gems to remove." if @gems.empty?
Injector.remove(@gems, {})
Installer.install(Bundler.root, Bundler.definition)
end
end
end

View File

@@ -1,75 +0,0 @@
# frozen_string_literal: true
module Bundler
class CLI::Show
attr_reader :options, :gem_name, :latest_specs
def initialize(options, gem_name)
@options = options
@gem_name = gem_name
@verbose = options[:verbose] || options[:outdated]
@latest_specs = fetch_latest_specs if @verbose
end
def run
Bundler.ui.silence do
Bundler.definition.validate_runtime!
Bundler.load.lock
end
if gem_name
if gem_name == "bundler"
path = File.expand_path("../../..", __dir__)
else
spec = Bundler::CLI::Common.select_spec(gem_name, :regex_match)
return unless spec
path = spec.full_gem_path
unless File.directory?(path)
return Bundler.ui.warn "The gem #{gem_name} has been deleted. It was installed at: #{path}"
end
end
return Bundler.ui.info(path)
end
if options[:paths]
Bundler.load.specs.sort_by(&:name).map do |s|
Bundler.ui.info s.full_gem_path
end
else
Bundler.ui.info "Gems included by the bundle:"
Bundler.load.specs.sort_by(&:name).each do |s|
desc = " * #{s.name} (#{s.version}#{s.git_version})"
if @verbose
latest = latest_specs.find {|l| l.name == s.name }
Bundler.ui.info <<-END.gsub(/^ +/, "")
#{desc}
\tSummary: #{s.summary || "No description available."}
\tHomepage: #{s.homepage || "No website available."}
\tStatus: #{outdated?(s, latest) ? "Outdated - #{s.version} < #{latest.version}" : "Up to date"}
END
else
Bundler.ui.info desc
end
end
end
end
private
def fetch_latest_specs
definition = Bundler.definition(true)
if options[:outdated]
Bundler.ui.info "Fetching remote specs for outdated check...\n\n"
Bundler.ui.silence { definition.resolve_remotely! }
else
definition.resolve_with_cache!
end
Bundler.reset!
definition.specs
end
def outdated?(current, latest)
return false unless latest
Gem::Version.new(current.version) < Gem::Version.new(latest.version)
end
end
end

View File

@@ -1,122 +0,0 @@
# frozen_string_literal: true
module Bundler
class CLI::Update
attr_reader :options, :gems
def initialize(options, gems)
@options = options
@gems = gems
end
def run
Bundler.ui.level = "warn" if options[:quiet]
update_bundler = options[:bundler]
Bundler.self_manager.update_bundler_and_restart_with_it_if_needed(update_bundler) if update_bundler
Plugin.gemfile_install(Bundler.default_gemfile) if Bundler.feature_flag.plugins?
sources = Array(options[:source])
groups = Array(options[:group]).map(&:to_sym)
full_update = gems.empty? && sources.empty? && groups.empty? && !options[:ruby] && !update_bundler
if full_update && !options[:all]
if Bundler.feature_flag.update_requires_all_flag?
raise InvalidOption, "To update everything, pass the `--all` flag."
end
SharedHelpers.major_deprecation 3, "Pass --all to `bundle update` to update everything"
elsif !full_update && options[:all]
raise InvalidOption, "Cannot specify --all along with specific options."
end
conservative = options[:conservative]
if full_update
if conservative
Bundler.definition(:conservative => conservative)
else
Bundler.definition(true)
end
else
unless Bundler.default_lockfile.exist?
raise GemfileLockNotFound, "This Bundle hasn't been installed yet. " \
"Run `bundle install` to update and install the bundled gems."
end
Bundler::CLI::Common.ensure_all_gems_in_lockfile!(gems)
if groups.any?
deps = Bundler.definition.dependencies.select {|d| (d.groups & groups).any? }
gems.concat(deps.map(&:name))
end
Bundler.definition(:gems => gems, :sources => sources, :ruby => options[:ruby],
:conservative => conservative,
:bundler => update_bundler)
end
Bundler::CLI::Common.configure_gem_version_promoter(Bundler.definition, options)
Bundler::Fetcher.disable_endpoint = options["full-index"]
opts = options.dup
opts["update"] = true
opts["local"] = options[:local]
Bundler.settings.set_command_option_if_given :jobs, opts["jobs"]
Bundler.definition.validate_runtime!
if locked_gems = Bundler.definition.locked_gems
previous_locked_info = locked_gems.specs.reduce({}) do |h, s|
h[s.name] = { :spec => s, :version => s.version, :source => s.source.identifier }
h
end
end
installer = Installer.install Bundler.root, Bundler.definition, opts
Bundler.load.cache if Bundler.app_cache.exist?
if CLI::Common.clean_after_install?
require_relative "clean"
Bundler::CLI::Clean.new(options).run
end
if locked_gems
gems.each do |name|
locked_info = previous_locked_info[name]
next unless locked_info
locked_spec = locked_info[:spec]
new_spec = Bundler.definition.specs[name].first
unless new_spec
unless locked_spec.match_platform(Bundler.local_platform)
Bundler.ui.warn "Bundler attempted to update #{name} but it was not considered because it is for a different platform from the current one"
end
next
end
locked_source = locked_info[:source]
new_source = new_spec.source.identifier
next if locked_source != new_source
new_version = new_spec.version
locked_version = locked_info[:version]
if new_version < locked_version
Bundler.ui.warn "Note: #{name} version regressed from #{locked_version} to #{new_version}"
elsif new_version == locked_version
Bundler.ui.warn "Bundler attempted to update #{name} but its version stayed the same"
end
end
end
Bundler.ui.confirm "Bundle updated!"
Bundler::CLI::Common.output_without_groups_message(:update)
Bundler::CLI::Common.output_post_install_messages installer.post_install_messages
Bundler::CLI::Common.output_fund_metadata_summary
end
end
end

View File

@@ -1,31 +0,0 @@
# frozen_string_literal: true
module Bundler
class CLI::Viz
attr_reader :options, :gem_name
def initialize(options)
@options = options
end
def run
# make sure we get the right `graphviz`. There is also a `graphviz`
# gem we're not built to support
gem "ruby-graphviz"
require "graphviz"
options[:without] = options[:without].join(":").tr(" ", ":").split(":")
output_file = File.expand_path(options[:file])
graph = Graph.new(Bundler.load, output_file, options[:version], options[:requirements], options[:format], options[:without])
graph.viz
rescue LoadError => e
Bundler.ui.error e.inspect
Bundler.ui.warn "Make sure you have the graphviz ruby gem. You can install it with:"
Bundler.ui.warn "`gem install ruby-graphviz`"
rescue StandardError => e
raise unless e.message.to_s.include?("GraphViz not installed or dot not in PATH")
Bundler.ui.error e.message
Bundler.ui.warn "Please install GraphViz. On a Mac with Homebrew, you can run `brew install graphviz`."
end
end
end

View File

@@ -1,119 +0,0 @@
# frozen_string_literal: true
require "pathname"
require "set"
module Bundler
class CompactIndexClient
DEBUG_MUTEX = Thread::Mutex.new
def self.debug
return unless ENV["DEBUG_COMPACT_INDEX"]
DEBUG_MUTEX.synchronize { warn("[#{self}] #{yield}") }
end
class Error < StandardError; end
require_relative "compact_index_client/cache"
require_relative "compact_index_client/updater"
attr_reader :directory
def initialize(directory, fetcher)
@directory = Pathname.new(directory)
@updater = Updater.new(fetcher)
@cache = Cache.new(@directory)
@endpoints = Set.new
@info_checksums_by_name = {}
@parsed_checksums = false
@mutex = Thread::Mutex.new
end
def execution_mode=(block)
Bundler::CompactIndexClient.debug { "execution_mode=" }
@endpoints = Set.new
@execution_mode = block
end
# @return [Lambda] A lambda that takes an array of inputs and a block, and
# maps the inputs with the block in parallel.
#
def execution_mode
@execution_mode || sequentially
end
def sequential_execution_mode!
self.execution_mode = sequentially
end
def sequentially
@sequentially ||= lambda do |inputs, &blk|
inputs.map(&blk)
end
end
def names
Bundler::CompactIndexClient.debug { "/names" }
update(@cache.names_path, "names")
@cache.names
end
def versions
Bundler::CompactIndexClient.debug { "/versions" }
update(@cache.versions_path, "versions")
versions, @info_checksums_by_name = @cache.versions
versions
end
def dependencies(names)
Bundler::CompactIndexClient.debug { "dependencies(#{names})" }
execution_mode.call(names) do |name|
update_info(name)
@cache.dependencies(name).map {|d| d.unshift(name) }
end.flatten(1)
end
def update_and_parse_checksums!
Bundler::CompactIndexClient.debug { "update_and_parse_checksums!" }
return @info_checksums_by_name if @parsed_checksums
update(@cache.versions_path, "versions")
@info_checksums_by_name = @cache.checksums
@parsed_checksums = true
end
private
def update(local_path, remote_path)
Bundler::CompactIndexClient.debug { "update(#{local_path}, #{remote_path})" }
unless synchronize { @endpoints.add?(remote_path) }
Bundler::CompactIndexClient.debug { "already fetched #{remote_path}" }
return
end
@updater.update(local_path, url(remote_path))
end
def update_info(name)
Bundler::CompactIndexClient.debug { "update_info(#{name})" }
path = @cache.info_path(name)
checksum = @updater.checksum_for_file(path)
unless existing = @info_checksums_by_name[name]
Bundler::CompactIndexClient.debug { "skipping updating info for #{name} since it is missing from versions" }
return
end
if checksum == existing
Bundler::CompactIndexClient.debug { "skipping updating info for #{name} since the versions checksum matches the local checksum" }
return
end
Bundler::CompactIndexClient.debug { "updating info for #{name} since the versions checksum #{existing} != the local checksum #{checksum}" }
update(path, "info/#{name}")
end
def url(path)
path
end
def synchronize
@mutex.synchronize { yield }
end
end
end

View File

@@ -1,101 +0,0 @@
# frozen_string_literal: true
require_relative "gem_parser"
module Bundler
class CompactIndexClient
class Cache
attr_reader :directory
def initialize(directory)
@directory = Pathname.new(directory).expand_path
info_roots.each do |dir|
SharedHelpers.filesystem_access(dir) do
FileUtils.mkdir_p(dir)
end
end
end
def names
lines(names_path)
end
def names_path
directory.join("names")
end
def versions
versions_by_name = Hash.new {|hash, key| hash[key] = [] }
info_checksums_by_name = {}
lines(versions_path).each do |line|
name, versions_string, info_checksum = line.split(" ", 3)
info_checksums_by_name[name] = info_checksum || ""
versions_string.split(",").each do |version|
if version.start_with?("-")
version = version[1..-1].split("-", 2).unshift(name)
versions_by_name[name].delete(version)
else
version = version.split("-", 2).unshift(name)
versions_by_name[name] << version
end
end
end
[versions_by_name, info_checksums_by_name]
end
def versions_path
directory.join("versions")
end
def checksums
checksums = {}
lines(versions_path).each do |line|
name, _, checksum = line.split(" ", 3)
checksums[name] = checksum
end
checksums
end
def dependencies(name)
lines(info_path(name)).map do |line|
parse_gem(line)
end
end
def info_path(name)
name = name.to_s
if /[^a-z0-9_-]/.match?(name)
name += "-#{SharedHelpers.digest(:MD5).hexdigest(name).downcase}"
info_roots.last.join(name)
else
info_roots.first.join(name)
end
end
private
def lines(path)
return [] unless path.file?
lines = SharedHelpers.filesystem_access(path, :read, &:read).split("\n")
header = lines.index("---")
header ? lines[header + 1..-1] : lines
end
def parse_gem(line)
@dependency_parser ||= GemParser.new
@dependency_parser.parse(line)
end
def info_roots
[
directory.join("info"),
directory.join("info-special-characters"),
]
end
end
end
end

View File

@@ -1,28 +0,0 @@
# frozen_string_literal: true
module Bundler
class CompactIndexClient
if defined?(Gem::Resolver::APISet::GemParser)
GemParser = Gem::Resolver::APISet::GemParser
else
class GemParser
def parse(line)
version_and_platform, rest = line.split(" ", 2)
version, platform = version_and_platform.split("-", 2)
dependencies, requirements = rest.split("|", 2).map {|s| s.split(",") } if rest
dependencies = dependencies ? dependencies.map {|d| parse_dependency(d) } : []
requirements = requirements ? requirements.map {|d| parse_dependency(d) } : []
[version, platform, dependencies, requirements]
end
private
def parse_dependency(string)
dependency = string.split(":")
dependency[-1] = dependency[-1].split("&") if dependency.size > 1
dependency
end
end
end
end
end

View File

@@ -1,117 +0,0 @@
# frozen_string_literal: true
require_relative "../vendored_fileutils"
module Bundler
class CompactIndexClient
class Updater
class MisMatchedChecksumError < Error
def initialize(path, server_checksum, local_checksum)
@path = path
@server_checksum = server_checksum
@local_checksum = local_checksum
end
def message
"The checksum of /#{@path} does not match the checksum provided by the server! Something is wrong " \
"(local checksum is #{@local_checksum.inspect}, was expecting #{@server_checksum.inspect})."
end
end
def initialize(fetcher)
@fetcher = fetcher
end
def update(local_path, remote_path, retrying = nil)
headers = {}
local_temp_path = local_path.sub(/$/, ".#{$$}")
local_temp_path = local_temp_path.sub(/$/, ".retrying") if retrying
local_temp_path = local_temp_path.sub(/$/, ".tmp")
# first try to fetch any new bytes on the existing file
if retrying.nil? && local_path.file?
copy_file local_path, local_temp_path
headers["If-None-Match"] = etag_for(local_temp_path)
headers["Range"] =
if local_temp_path.size.nonzero?
# Subtract a byte to ensure the range won't be empty.
# Avoids 416 (Range Not Satisfiable) responses.
"bytes=#{local_temp_path.size - 1}-"
else
"bytes=#{local_temp_path.size}-"
end
end
response = @fetcher.call(remote_path, headers)
return nil if response.is_a?(Net::HTTPNotModified)
content = response.body
etag = (response["ETag"] || "").gsub(%r{\AW/}, "")
correct_response = SharedHelpers.filesystem_access(local_temp_path) do
if response.is_a?(Net::HTTPPartialContent) && local_temp_path.size.nonzero?
local_temp_path.open("a") {|f| f << slice_body(content, 1..-1) }
etag_for(local_temp_path) == etag
else
local_temp_path.open("wb") {|f| f << content }
etag.length.zero? || etag_for(local_temp_path) == etag
end
end
if correct_response
SharedHelpers.filesystem_access(local_path) do
FileUtils.mv(local_temp_path, local_path)
end
return nil
end
if retrying
raise MisMatchedChecksumError.new(remote_path, etag, etag_for(local_temp_path))
end
update(local_path, remote_path, :retrying)
rescue Zlib::GzipFile::Error
raise Bundler::HTTPError
ensure
FileUtils.remove_file(local_temp_path) if File.exist?(local_temp_path)
end
def etag_for(path)
sum = checksum_for_file(path)
sum ? %("#{sum}") : nil
end
def slice_body(body, range)
body.byteslice(range)
end
def checksum_for_file(path)
return nil unless path.file?
# This must use File.read instead of Digest.file().hexdigest
# because we need to preserve \n line endings on windows when calculating
# the checksum
SharedHelpers.filesystem_access(path, :read) do
SharedHelpers.digest(:MD5).hexdigest(File.read(path))
end
end
private
def copy_file(source, dest)
SharedHelpers.filesystem_access(source, :read) do
File.open(source, "r") do |s|
SharedHelpers.filesystem_access(dest, :write) do
File.open(dest, "wb", s.stat.mode) do |f|
IO.copy_stream(s, f)
end
end
end
end
end
end
end
end

View File

@@ -1,7 +0,0 @@
# frozen_string_literal: true
module Bundler
WINDOWS = RbConfig::CONFIG["host_os"] =~ /(msdos|mswin|djgpp|mingw)/
FREEBSD = RbConfig::CONFIG["host_os"].to_s.include?("bsd")
NULL = WINDOWS ? "NUL" : "/dev/null"
end

View File

@@ -1,108 +0,0 @@
# frozen_string_literal: true
module Bundler
# Returns current version of Ruby
#
# @return [CurrentRuby] Current version of Ruby
def self.current_ruby
@current_ruby ||= CurrentRuby.new
end
class CurrentRuby
KNOWN_MINOR_VERSIONS = %w[
1.8
1.9
2.0
2.1
2.2
2.3
2.4
2.5
2.6
2.7
3.0
3.1
3.2
3.3
].freeze
KNOWN_MAJOR_VERSIONS = KNOWN_MINOR_VERSIONS.map {|v| v.split(".", 2).first }.uniq.freeze
KNOWN_PLATFORMS = %w[
jruby
maglev
mingw
mri
mswin
mswin64
rbx
ruby
truffleruby
windows
x64_mingw
].freeze
def ruby?
return true if Bundler::GemHelpers.generic_local_platform == Gem::Platform::RUBY
!windows? && (RUBY_ENGINE == "ruby" || RUBY_ENGINE == "rbx" || RUBY_ENGINE == "maglev" || RUBY_ENGINE == "truffleruby")
end
def mri?
!windows? && RUBY_ENGINE == "ruby"
end
def rbx?
ruby? && RUBY_ENGINE == "rbx"
end
def jruby?
RUBY_ENGINE == "jruby"
end
def maglev?
RUBY_ENGINE == "maglev"
end
def truffleruby?
RUBY_ENGINE == "truffleruby"
end
def windows?
Gem.win_platform?
end
def mswin?
# For backwards compatibility
windows?
# TODO: This should correctly be:
# windows? && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mswin32" && Bundler.local_platform.cpu == "x86"
end
def mswin64?
windows? && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mswin64" && Bundler.local_platform.cpu == "x64"
end
def mingw?
windows? && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mingw32" && Bundler.local_platform.cpu != "x64"
end
def x64_mingw?
Gem.win_platform? && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os.start_with?("mingw") && Bundler.local_platform.cpu == "x64"
end
(KNOWN_MINOR_VERSIONS + KNOWN_MAJOR_VERSIONS).each do |version|
trimmed_version = version.tr(".", "")
define_method(:"on_#{trimmed_version}?") do
RUBY_VERSION.start_with?("#{version}.")
end
KNOWN_PLATFORMS.each do |platform|
define_method(:"#{platform}_#{trimmed_version}?") do
send(:"#{platform}?") && send(:"on_#{trimmed_version}?")
end
end
end
end
end

View File

@@ -1,961 +0,0 @@
# frozen_string_literal: true
require_relative "lockfile_parser"
module Bundler
class Definition
include GemHelpers
class << self
# Do not create or modify a lockfile (Makes #lock a noop)
attr_accessor :no_lock
end
attr_reader(
:dependencies,
:locked_deps,
:locked_gems,
:platforms,
:ruby_version,
:lockfile,
:gemfiles
)
# Given a gemfile and lockfile creates a Bundler definition
#
# @param gemfile [Pathname] Path to Gemfile
# @param lockfile [Pathname,nil] Path to Gemfile.lock
# @param unlock [Hash, Boolean, nil] Gems that have been requested
# to be updated or true if all gems should be updated
# @return [Bundler::Definition]
def self.build(gemfile, lockfile, unlock)
unlock ||= {}
gemfile = Pathname.new(gemfile).expand_path
raise GemfileNotFound, "#{gemfile} not found" unless gemfile.file?
Dsl.evaluate(gemfile, lockfile, unlock)
end
#
# How does the new system work?
#
# * Load information from Gemfile and Lockfile
# * Invalidate stale locked specs
# * All specs from stale source are stale
# * All specs that are reachable only through a stale
# dependency are stale.
# * If all fresh dependencies are satisfied by the locked
# specs, then we can try to resolve locally.
#
# @param lockfile [Pathname] Path to Gemfile.lock
# @param dependencies [Array(Bundler::Dependency)] array of dependencies from Gemfile
# @param sources [Bundler::SourceList]
# @param unlock [Hash, Boolean, nil] Gems that have been requested
# to be updated or true if all gems should be updated
# @param ruby_version [Bundler::RubyVersion, nil] Requested Ruby Version
# @param optional_groups [Array(String)] A list of optional groups
def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil, optional_groups = [], gemfiles = [])
if [true, false].include?(unlock)
@unlocking_bundler = false
@unlocking = unlock
else
@unlocking_bundler = unlock.delete(:bundler)
@unlocking = unlock.any? {|_k, v| !Array(v).empty? }
end
@dependencies = dependencies
@sources = sources
@unlock = unlock
@optional_groups = optional_groups
@remote = false
@prefer_local = false
@specs = nil
@ruby_version = ruby_version
@gemfiles = gemfiles
@lockfile = lockfile
@lockfile_contents = String.new
@locked_bundler_version = nil
@resolved_bundler_version = nil
@locked_ruby_version = nil
@new_platform = nil
@removed_platform = nil
if lockfile && File.exist?(lockfile)
@lockfile_contents = Bundler.read_file(lockfile)
@locked_gems = LockfileParser.new(@lockfile_contents)
@locked_platforms = @locked_gems.platforms
@platforms = @locked_platforms.dup
@locked_bundler_version = @locked_gems.bundler_version
@locked_ruby_version = @locked_gems.ruby_version
@originally_locked_specs = SpecSet.new(@locked_gems.specs)
if unlock != true
@locked_deps = @locked_gems.dependencies
@locked_specs = @originally_locked_specs
@locked_sources = @locked_gems.sources
else
@unlock = {}
@locked_deps = {}
@locked_specs = SpecSet.new([])
@locked_sources = []
end
else
@unlock = {}
@platforms = []
@locked_gems = nil
@locked_deps = {}
@locked_specs = SpecSet.new([])
@originally_locked_specs = @locked_specs
@locked_sources = []
@locked_platforms = []
end
locked_gem_sources = @locked_sources.select {|s| s.is_a?(Source::Rubygems) }
@multisource_allowed = locked_gem_sources.size == 1 && locked_gem_sources.first.multiple_remotes? && Bundler.frozen_bundle?
if @multisource_allowed
unless sources.aggregate_global_source?
msg = "Your lockfile contains a single rubygems source section with multiple remotes, which is insecure. Make sure you run `bundle install` in non frozen mode and commit the result to make your lockfile secure."
Bundler::SharedHelpers.major_deprecation 2, msg
end
@sources.merged_gem_lockfile_sections!(locked_gem_sources.first)
end
@unlock[:sources] ||= []
@unlock[:ruby] ||= if @ruby_version && locked_ruby_version_object
@ruby_version.diff(locked_ruby_version_object)
end
@unlocking ||= @unlock[:ruby] ||= (!@locked_ruby_version ^ !@ruby_version)
add_current_platform unless Bundler.frozen_bundle?
converge_path_sources_to_gemspec_sources
@path_changes = converge_paths
@source_changes = converge_sources
if @unlock[:conservative]
@unlock[:gems] ||= @dependencies.map(&:name)
else
eager_unlock = (@unlock[:gems] || []).map {|name| Dependency.new(name, ">= 0") }
@unlock[:gems] = @locked_specs.for(eager_unlock, false, platforms).map(&:name).uniq
end
@dependency_changes = converge_dependencies
@local_changes = converge_locals
@missing_lockfile_dep = check_missing_lockfile_dep
end
def gem_version_promoter
@gem_version_promoter ||= GemVersionPromoter.new
end
def resolve_only_locally!
@remote = false
sources.local_only!
resolve
end
def resolve_with_cache!
sources.cached!
resolve
end
def resolve_remotely!
@remote = true
sources.remote!
resolve
end
def resolution_mode=(options)
if options["local"]
@remote = false
else
@remote = true
@prefer_local = options["prefer-local"]
end
end
def setup_sources_for_resolve
if @remote == false
sources.cached!
else
sources.remote!
end
end
# For given dependency list returns a SpecSet with Gemspec of all the required
# dependencies.
# 1. The method first resolves the dependencies specified in Gemfile
# 2. After that it tries and fetches gemspec of resolved dependencies
#
# @return [Bundler::SpecSet]
def specs
@specs ||= materialize(requested_dependencies)
end
def new_specs
specs - @locked_specs
end
def removed_specs
@locked_specs - specs
end
def missing_specs
resolve.materialize(requested_dependencies).missing_specs
end
def missing_specs?
missing = missing_specs
return false if missing.empty?
Bundler.ui.debug "The definition is missing #{missing.map(&:full_name)}"
true
rescue BundlerError => e
@resolve = nil
@resolver = nil
@resolution_packages = nil
@specs = nil
@gem_version_promoter = nil
Bundler.ui.debug "The definition is missing dependencies, failed to resolve & materialize locally (#{e})"
true
end
def requested_specs
specs_for(requested_groups)
end
def requested_dependencies
dependencies_for(requested_groups)
end
def current_dependencies
filter_relevant(dependencies)
end
def current_locked_dependencies
filter_relevant(locked_dependencies)
end
def filter_relevant(dependencies)
dependencies.select do |d|
d.should_include? && !d.gem_platforms([generic_local_platform]).empty?
end
end
def locked_dependencies
@locked_deps.values
end
def new_deps
@new_deps ||= @dependencies - locked_dependencies
end
def deleted_deps
@deleted_deps ||= locked_dependencies - @dependencies
end
def specs_for(groups)
return specs if groups.empty?
deps = dependencies_for(groups)
materialize(deps)
end
def dependencies_for(groups)
groups.map!(&:to_sym)
current_dependencies.reject do |d|
(d.groups & groups).empty?
end
end
# Resolve all the dependencies specified in Gemfile. It ensures that
# dependencies that have been already resolved via locked file and are fresh
# are reused when resolving dependencies
#
# @return [SpecSet] resolved dependencies
def resolve
@resolve ||= if Bundler.frozen_bundle?
Bundler.ui.debug "Frozen, using resolution from the lockfile"
@locked_specs
elsif no_resolve_needed?
if deleted_deps.any?
Bundler.ui.debug "Some dependencies were deleted, using a subset of the resolution from the lockfile"
SpecSet.new(filter_specs(@locked_specs, @dependencies - deleted_deps))
else
Bundler.ui.debug "Found no changes, using resolution from the lockfile"
if @removed_platform || @locked_gems.may_include_redundant_platform_specific_gems?
SpecSet.new(filter_specs(@locked_specs, @dependencies))
else
@locked_specs
end
end
else
Bundler.ui.debug "Found changes from the lockfile, re-resolving dependencies because #{change_reason}"
start_resolution
end
end
def spec_git_paths
sources.git_sources.map {|s| File.realpath(s.path) if File.exist?(s.path) }.compact
end
def groups
dependencies.map(&:groups).flatten.uniq
end
def lock(file, preserve_unknown_sections = false)
return if Definition.no_lock
contents = to_lock
# Convert to \r\n if the existing lock has them
# i.e., Windows with `git config core.autocrlf=true`
contents.gsub!(/\n/, "\r\n") if @lockfile_contents.match?("\r\n")
if @locked_bundler_version
locked_major = @locked_bundler_version.segments.first
current_major = bundler_version_to_lock.segments.first
updating_major = locked_major < current_major
end
preserve_unknown_sections ||= !updating_major && (Bundler.frozen_bundle? || !(unlocking? || @unlocking_bundler))
return if file && File.exist?(file) && lockfiles_equal?(@lockfile_contents, contents, preserve_unknown_sections)
if Bundler.frozen_bundle?
Bundler.ui.error "Cannot write a changed lockfile while frozen."
return
end
SharedHelpers.filesystem_access(file) do |p|
File.open(p, "wb") {|f| f.puts(contents) }
end
end
def locked_ruby_version
return unless ruby_version
if @unlock[:ruby] || !@locked_ruby_version
Bundler::RubyVersion.system
else
@locked_ruby_version
end
end
def locked_ruby_version_object
return unless @locked_ruby_version
@locked_ruby_version_object ||= begin
unless version = RubyVersion.from_string(@locked_ruby_version)
raise LockfileError, "The Ruby version #{@locked_ruby_version} from " \
"#{@lockfile} could not be parsed. " \
"Try running bundle update --ruby to resolve this."
end
version
end
end
def bundler_version_to_lock
@resolved_bundler_version || Bundler.gem_version
end
def to_lock
require_relative "lockfile_generator"
LockfileGenerator.generate(self)
end
def ensure_equivalent_gemfile_and_lockfile(explicit_flag = false)
added = []
deleted = []
changed = []
new_platforms = @platforms - @locked_platforms
deleted_platforms = @locked_platforms - @platforms
added.concat new_platforms.map {|p| "* platform: #{p}" }
deleted.concat deleted_platforms.map {|p| "* platform: #{p}" }
added.concat new_deps.map {|d| "* #{pretty_dep(d)}" } if new_deps.any?
deleted.concat deleted_deps.map {|d| "* #{pretty_dep(d)}" } if deleted_deps.any?
both_sources = Hash.new {|h, k| h[k] = [] }
current_dependencies.each {|d| both_sources[d.name][0] = d }
current_locked_dependencies.each {|d| both_sources[d.name][1] = d }
both_sources.each do |name, (dep, lock_dep)|
next if dep.nil? || lock_dep.nil?
gemfile_source = dep.source || default_source
lock_source = lock_dep.source || default_source
next if lock_source.include?(gemfile_source)
gemfile_source_name = dep.source ? gemfile_source.to_gemfile : "no specified source"
lockfile_source_name = lock_dep.source ? lock_source.to_gemfile : "no specified source"
changed << "* #{name} from `#{lockfile_source_name}` to `#{gemfile_source_name}`"
end
reason = change_reason
msg = String.new
msg << "#{reason.capitalize.strip}, but the lockfile can't be updated because frozen mode is set"
msg << "\n\nYou have added to the Gemfile:\n" << added.join("\n") if added.any?
msg << "\n\nYou have deleted from the Gemfile:\n" << deleted.join("\n") if deleted.any?
msg << "\n\nYou have changed in the Gemfile:\n" << changed.join("\n") if changed.any?
msg << "\n\nRun `bundle install` elsewhere and add the updated #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)} to version control.\n"
unless explicit_flag
suggested_command = unless Bundler.settings.locations("frozen").keys.include?(:env)
"bundle config set frozen false"
end
msg << "If this is a development machine, remove the #{Bundler.default_gemfile.relative_path_from(SharedHelpers.pwd)} " \
"freeze by running `#{suggested_command}`." if suggested_command
end
raise ProductionError, msg if added.any? || deleted.any? || changed.any? || !nothing_changed?
end
def validate_runtime!
validate_ruby!
validate_platforms!
end
def validate_ruby!
return unless ruby_version
if diff = ruby_version.diff(Bundler::RubyVersion.system)
problem, expected, actual = diff
msg = case problem
when :engine
"Your Ruby engine is #{actual}, but your Gemfile specified #{expected}"
when :version
"Your Ruby version is #{actual}, but your Gemfile specified #{expected}"
when :engine_version
"Your #{Bundler::RubyVersion.system.engine} version is #{actual}, but your Gemfile specified #{ruby_version.engine} #{expected}"
when :patchlevel
if !expected.is_a?(String)
"The Ruby patchlevel in your Gemfile must be a string"
else
"Your Ruby patchlevel is #{actual}, but your Gemfile specified #{expected}"
end
end
raise RubyVersionMismatch, msg
end
end
def validate_platforms!
return if current_platform_locked?
raise ProductionError, "Your bundle only supports platforms #{@platforms.map(&:to_s)} " \
"but your local platform is #{Bundler.local_platform}. " \
"Add the current platform to the lockfile with\n`bundle lock --add-platform #{Bundler.local_platform}` and try again."
end
def add_platform(platform)
@new_platform ||= !@platforms.include?(platform)
@platforms |= [platform]
end
def remove_platform(platform)
removed_platform = @platforms.delete(Gem::Platform.new(platform))
@removed_platform ||= removed_platform
return if removed_platform
raise InvalidOption, "Unable to remove the platform `#{platform}` since the only platforms are #{@platforms.join ", "}"
end
def most_specific_locked_platform
@platforms.min_by do |bundle_platform|
platform_specificity_match(bundle_platform, local_platform)
end
end
attr_reader :sources
private :sources
def nothing_changed?
!@source_changes && !@dependency_changes && !@new_platform && !@path_changes && !@local_changes && !@missing_lockfile_dep && !@unlocking_bundler
end
def no_resolve_needed?
!unlocking? && nothing_changed?
end
def unlocking?
@unlocking
end
private
def resolver
@resolver ||= Resolver.new(resolution_packages, gem_version_promoter)
end
def expanded_dependencies
dependencies_with_bundler + metadata_dependencies
end
def dependencies_with_bundler
return dependencies unless @unlocking_bundler
return dependencies if dependencies.map(&:name).include?("bundler")
[Dependency.new("bundler", @unlocking_bundler)] + dependencies
end
def resolution_packages
@resolution_packages ||= begin
last_resolve = converge_locked_specs
remove_ruby_from_platforms_if_necessary!(current_dependencies)
packages = Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, :locked_specs => @originally_locked_specs, :unlock => @unlock[:gems], :prerelease => gem_version_promoter.pre?)
additional_base_requirements_for_resolve(packages, last_resolve)
end
end
def filter_specs(specs, deps)
SpecSet.new(specs).for(deps, false, platforms)
end
def materialize(dependencies)
specs = resolve.materialize(dependencies)
missing_specs = specs.missing_specs
if missing_specs.any?
missing_specs.each do |s|
locked_gem = @locked_specs[s.name].last
next if locked_gem.nil? || locked_gem.version != s.version || !@remote
raise GemNotFound, "Your bundle is locked to #{locked_gem} from #{locked_gem.source}, but that version can " \
"no longer be found in that source. That means the author of #{locked_gem} has removed it. " \
"You'll need to update your bundle to a version other than #{locked_gem} that hasn't been " \
"removed in order to install."
end
missing_specs_list = missing_specs.group_by(&:source).map do |source, missing_specs_for_source|
"#{missing_specs_for_source.map(&:full_name).join(", ")} in #{source}"
end
raise GemNotFound, "Could not find #{missing_specs_list.join(" nor ")}"
end
incomplete_specs = specs.incomplete_specs
loop do
break if incomplete_specs.empty?
Bundler.ui.debug("The lockfile does not have all gems needed for the current platform though, Bundler will still re-resolve dependencies")
setup_sources_for_resolve
resolution_packages.delete(incomplete_specs)
@resolve = start_resolution
specs = resolve.materialize(dependencies)
still_incomplete_specs = specs.incomplete_specs
if still_incomplete_specs == incomplete_specs
package = resolution_packages.get_package(incomplete_specs.first.name)
resolver.raise_not_found! package
end
incomplete_specs = still_incomplete_specs
end
bundler = sources.metadata_source.specs.search(["bundler", Bundler.gem_version]).last
specs["bundler"] = bundler
specs
end
def start_resolution
result = resolver.start
@resolved_bundler_version = result.find {|spec| spec.name == "bundler" }&.version
SpecSet.new(SpecSet.new(result).for(dependencies, false, @platforms))
end
def precompute_source_requirements_for_indirect_dependencies?
sources.non_global_rubygems_sources.all?(&:dependency_api_available?) && !sources.aggregate_global_source?
end
def pin_locally_available_names(source_requirements)
source_requirements.each_with_object({}) do |(name, original_source), new_source_requirements|
local_source = original_source.dup
local_source.local_only!
new_source_requirements[name] = if local_source.specs.search(name).any?
local_source
else
original_source
end
end
end
def current_ruby_platform_locked?
return false unless generic_local_platform == Gem::Platform::RUBY
return false if Bundler.settings[:force_ruby_platform] && !@platforms.include?(Gem::Platform::RUBY)
current_platform_locked?
end
def current_platform_locked?
@platforms.any? do |bundle_platform|
MatchPlatform.platforms_match?(bundle_platform, Bundler.local_platform)
end
end
def add_current_platform
return if current_ruby_platform_locked?
add_platform(local_platform)
end
def change_reason
if unlocking?
unlock_reason = @unlock.reject {|_k, v| Array(v).empty? }.map do |k, v|
if v == true
k.to_s
else
v = Array(v)
"#{k}: (#{v.join(", ")})"
end
end.join(", ")
return "bundler is unlocking #{unlock_reason}"
end
[
[@source_changes, "the list of sources changed"],
[@dependency_changes, "the dependencies in your gemfile changed"],
[@new_platform, "you added a new platform to your gemfile"],
[@path_changes, "the gemspecs for path gems changed"],
[@local_changes, "the gemspecs for git local gems changed"],
[@missing_lockfile_dep, "your lock file is missing \"#{@missing_lockfile_dep}\""],
[@unlocking_bundler, "an update to the version of Bundler itself was requested"],
].select(&:first).map(&:last).join(", ")
end
def pretty_dep(dep)
SharedHelpers.pretty_dependency(dep)
end
# Check if the specs of the given source changed
# according to the locked source.
def specs_changed?(source)
locked = @locked_sources.find {|s| s == source }
!locked || dependencies_for_source_changed?(source, locked) || specs_for_source_changed?(source)
end
def dependencies_for_source_changed?(source, locked_source = source)
deps_for_source = @dependencies.select {|s| s.source == source }
locked_deps_for_source = locked_dependencies.select {|dep| dep.source == locked_source }
deps_for_source.uniq.sort != locked_deps_for_source.sort
end
def specs_for_source_changed?(source)
locked_index = Index.new
locked_index.use(@locked_specs.select {|s| source.can_lock?(s) })
# order here matters, since Index#== is checking source.specs.include?(locked_index)
locked_index != source.specs
rescue PathError, GitError => e
Bundler.ui.debug "Assuming that #{source} has not changed since fetching its specs errored (#{e})"
false
end
# Get all locals and override their matching sources.
# Return true if any of the locals changed (for example,
# they point to a new revision) or depend on new specs.
def converge_locals
locals = []
Bundler.settings.local_overrides.map do |k, v|
spec = @dependencies.find {|s| s.name == k }
source = spec&.source
if source&.respond_to?(:local_override!)
source.unlock! if @unlock[:gems].include?(spec.name)
locals << [source, source.local_override!(v)]
end
end
sources_with_changes = locals.select do |source, changed|
changed || specs_changed?(source)
end.map(&:first)
!sources_with_changes.each {|source| @unlock[:sources] << source.name }.empty?
end
def check_missing_lockfile_dep
all_locked_specs = @locked_specs.map(&:name) << "bundler"
missing = @locked_specs.select do |s|
s.dependencies.any? {|dep| !all_locked_specs.include?(dep.name) }
end
if missing.any?
@locked_specs.delete(missing)
return missing.first.name
end
return if @dependency_changes
current_dependencies.find do |d|
@locked_specs[d.name].empty? && d.name != "bundler"
end&.name
end
def converge_paths
sources.path_sources.any? do |source|
specs_changed?(source)
end
end
def converge_path_source_to_gemspec_source(source)
return source unless source.instance_of?(Source::Path)
gemspec_source = sources.path_sources.find {|s| s.is_a?(Source::Gemspec) && s.as_path_source == source }
gemspec_source || source
end
def converge_path_sources_to_gemspec_sources
@locked_sources.map! do |source|
converge_path_source_to_gemspec_source(source)
end
@locked_specs.each do |spec|
spec.source &&= converge_path_source_to_gemspec_source(spec.source)
end
@locked_deps.each do |_, dep|
dep.source &&= converge_path_source_to_gemspec_source(dep.source)
end
end
def converge_sources
# Replace the sources from the Gemfile with the sources from the Gemfile.lock,
# if they exist in the Gemfile.lock and are `==`. If you can't find an equivalent
# source in the Gemfile.lock, use the one from the Gemfile.
changes = sources.replace_sources!(@locked_sources)
sources.all_sources.each do |source|
# If the source is unlockable and the current command allows an unlock of
# the source (for example, you are doing a `bundle update <foo>` of a git-pinned
# gem), unlock it. For git sources, this means to unlock the revision, which
# will cause the `ref` used to be the most recent for the branch (or master) if
# an explicit `ref` is not used.
if source.respond_to?(:unlock!) && @unlock[:sources].include?(source.name)
source.unlock!
changes = true
end
end
changes
end
def converge_dependencies
changes = false
@dependencies.each do |dep|
if dep.source
dep.source = sources.get(dep.source)
end
next if unlocking?
unless locked_dep = @locked_deps[dep.name]
changes = true
next
end
# Gem::Dependency#== matches Gem::Dependency#type. As the lockfile
# doesn't carry a notion of the dependency type, if you use
# add_development_dependency in a gemspec that's loaded with the gemspec
# directive, the lockfile dependencies and resolved dependencies end up
# with a mismatch on #type. Work around that by setting the type on the
# dep from the lockfile.
locked_dep.instance_variable_set(:@type, dep.type)
# We already know the name matches from the hash lookup
# so we only need to check the requirement now
changes ||= dep.requirement != locked_dep.requirement
end
changes
end
# Remove elements from the locked specs that are expired. This will most
# commonly happen if the Gemfile has changed since the lockfile was last
# generated
def converge_locked_specs
converged = converge_specs(@locked_specs)
resolve = SpecSet.new(converged.reject {|s| @unlock[:gems].include?(s.name) })
diff = nil
# Now, we unlock any sources that do not have anymore gems pinned to it
sources.all_sources.each do |source|
next unless source.respond_to?(:unlock!)
unless resolve.any? {|s| s.source == source }
diff ||= @locked_specs.to_a - resolve.to_a
source.unlock! if diff.any? {|s| s.source == source }
end
end
resolve
end
def converge_specs(specs)
converged = []
deps = []
@specs_that_changed_sources = []
specs.each do |s|
name = s.name
dep = @dependencies.find {|d| s.satisfies?(d) }
lockfile_source = s.source
if dep
gemfile_source = dep.source || default_source
@specs_that_changed_sources << s if gemfile_source != lockfile_source
deps << dep if !dep.source || lockfile_source.include?(dep.source)
@unlock[:gems] << name if lockfile_source.include?(dep.source) && lockfile_source != gemfile_source
# Replace the locked dependency's source with the equivalent source from the Gemfile
s.source = gemfile_source
else
# Replace the locked dependency's source with the default source, if the locked source is no longer in the Gemfile
s.source = default_source unless sources.get(lockfile_source)
end
next if @unlock[:sources].include?(s.source.name)
# Path sources have special logic
if s.source.instance_of?(Source::Path) || s.source.instance_of?(Source::Gemspec)
new_specs = begin
s.source.specs
rescue PathError
# if we won't need the source (according to the lockfile),
# don't error if the path source isn't available
next if specs.
for(requested_dependencies, false).
none? {|locked_spec| locked_spec.source == s.source }
raise
end
new_spec = new_specs[s].first
if new_spec
s.dependencies.replace(new_spec.dependencies)
else
# If the spec is no longer in the path source, unlock it. This
# commonly happens if the version changed in the gemspec
@unlock[:gems] << name
end
end
if dep.nil? && requested_dependencies.find {|d| name == d.name }
@unlock[:gems] << s.name
else
converged << s
end
end
filter_specs(converged, deps)
end
def metadata_dependencies
@metadata_dependencies ||= [
Dependency.new("Ruby\0", Gem.ruby_version),
Dependency.new("RubyGems\0", Gem::VERSION),
]
end
def source_requirements
# Record the specs available in each gem's source, so that those
# specs will be available later when the resolver knows where to
# look for that gemspec (or its dependencies)
source_requirements = if precompute_source_requirements_for_indirect_dependencies?
all_requirements = source_map.all_requirements
all_requirements = pin_locally_available_names(all_requirements) if @prefer_local
{ :default => default_source }.merge(all_requirements)
else
{ :default => Source::RubygemsAggregate.new(sources, source_map) }.merge(source_map.direct_requirements)
end
source_requirements.merge!(source_map.locked_requirements) unless @remote
metadata_dependencies.each do |dep|
source_requirements[dep.name] = sources.metadata_source
end
default_bundler_source = source_requirements["bundler"] || default_source
if @unlocking_bundler
default_bundler_source.add_dependency_names("bundler")
else
source_requirements[:default_bundler] = default_bundler_source
source_requirements["bundler"] = sources.metadata_source # needs to come last to override
end
verify_changed_sources!
source_requirements
end
def default_source
sources.default_source
end
def verify_changed_sources!
@specs_that_changed_sources.each do |s|
if s.source.specs.search(s.name).empty?
raise GemNotFound, "Could not find gem '#{s.name}' in #{s.source}"
end
end
end
def requested_groups
values = groups - Bundler.settings[:without] - @optional_groups + Bundler.settings[:with]
values &= Bundler.settings[:only] unless Bundler.settings[:only].empty?
values
end
def lockfiles_equal?(current, proposed, preserve_unknown_sections)
if preserve_unknown_sections
sections_to_ignore = LockfileParser.sections_to_ignore(@locked_bundler_version)
sections_to_ignore += LockfileParser.unknown_sections_in_lockfile(current)
sections_to_ignore << LockfileParser::RUBY
sections_to_ignore << LockfileParser::BUNDLED unless @unlocking_bundler
pattern = /#{Regexp.union(sections_to_ignore)}\n(\s{2,}.*\n)+/
whitespace_cleanup = /\n{2,}/
current = current.gsub(pattern, "\n").gsub(whitespace_cleanup, "\n\n").strip
proposed = proposed.gsub(pattern, "\n").gsub(whitespace_cleanup, "\n\n").strip
end
current == proposed
end
def additional_base_requirements_for_resolve(resolution_packages, last_resolve)
return resolution_packages unless @locked_gems && !sources.expired_sources?(@locked_gems.sources)
converge_specs(@originally_locked_specs - last_resolve).each do |locked_spec|
next if locked_spec.source.is_a?(Source::Path)
resolution_packages.base_requirements[locked_spec.name] = Gem::Requirement.new(">= #{locked_spec.version}")
end
resolution_packages
end
def remove_ruby_from_platforms_if_necessary!(dependencies)
return if Bundler.frozen_bundle? ||
Bundler.local_platform == Gem::Platform::RUBY ||
!platforms.include?(Gem::Platform::RUBY) ||
(@new_platform && platforms.last == Gem::Platform::RUBY) ||
@path_changes ||
@dependency_changes ||
!@originally_locked_specs.incomplete_ruby_specs?(dependencies)
remove_platform(Gem::Platform::RUBY)
add_current_platform
end
def source_map
@source_map ||= SourceMap.new(sources, dependencies, @locked_specs)
end
end
end

View File

@@ -1,97 +0,0 @@
# frozen_string_literal: true
require "rubygems/dependency"
require_relative "shared_helpers"
require_relative "rubygems_ext"
module Bundler
class Dependency < Gem::Dependency
attr_reader :autorequire
attr_reader :groups, :platforms, :gemfile, :path, :git, :github, :branch, :ref
ALL_RUBY_VERSIONS = ((18..27).to_a + (30..33).to_a).freeze
PLATFORM_MAP = {
:ruby => [Gem::Platform::RUBY, ALL_RUBY_VERSIONS],
:mri => [Gem::Platform::RUBY, ALL_RUBY_VERSIONS],
:rbx => [Gem::Platform::RUBY],
:truffleruby => [Gem::Platform::RUBY],
:jruby => [Gem::Platform::JAVA, [18, 19]],
:windows => [Gem::Platform::WINDOWS, ALL_RUBY_VERSIONS],
:mswin => [Gem::Platform::MSWIN, ALL_RUBY_VERSIONS],
:mswin64 => [Gem::Platform::MSWIN64, ALL_RUBY_VERSIONS - [18]],
:mingw => [Gem::Platform::MINGW, ALL_RUBY_VERSIONS],
:x64_mingw => [Gem::Platform::X64_MINGW, ALL_RUBY_VERSIONS - [18, 19]],
}.each_with_object({}) do |(platform, spec), hash|
hash[platform] = spec[0]
spec[1]&.each {|version| hash[:"#{platform}_#{version}"] = spec[0] }
end.freeze
def initialize(name, version, options = {}, &blk)
type = options["type"] || :runtime
super(name, version, type)
@autorequire = nil
@groups = Array(options["group"] || :default).map(&:to_sym)
@source = options["source"]
@path = options["path"]
@git = options["git"]
@github = options["github"]
@branch = options["branch"]
@ref = options["ref"]
@platforms = Array(options["platforms"])
@env = options["env"]
@should_include = options.fetch("should_include", true)
@gemfile = options["gemfile"]
@force_ruby_platform = options["force_ruby_platform"] if options.key?("force_ruby_platform")
@autorequire = Array(options["require"] || []) if options.key?("require")
end
# Returns the platforms this dependency is valid for, in the same order as
# passed in the `valid_platforms` parameter
def gem_platforms(valid_platforms)
return [Gem::Platform::RUBY] if force_ruby_platform
return valid_platforms if @platforms.empty?
valid_platforms.select {|p| expanded_platforms.include?(GemHelpers.generic(p)) }
end
def expanded_platforms
@expanded_platforms ||= @platforms.map {|pl| PLATFORM_MAP[pl] }.compact.flatten.uniq
end
def should_include?
@should_include && current_env? && current_platform?
end
def current_env?
return true unless @env
if @env.is_a?(Hash)
@env.all? do |key, val|
ENV[key.to_s] && (val.is_a?(String) ? ENV[key.to_s] == val : ENV[key.to_s] =~ val)
end
else
ENV[@env.to_s]
end
end
def current_platform?
return true if @platforms.empty?
@platforms.any? do |p|
Bundler.current_ruby.send("#{p}?")
end
end
def to_lock
out = super
out << "!" if source
out
end
def specific?
super
rescue NoMethodError
requirement != ">= 0"
end
end
end

View File

@@ -1,69 +0,0 @@
# frozen_string_literal: true
require_relative "shared_helpers"
Bundler::SharedHelpers.major_deprecation 2, "Bundler no longer integrates with " \
"Capistrano, but Capistrano provides its own integration with " \
"Bundler via the capistrano-bundler gem. Use it instead."
module Bundler
class Deployment
def self.define_task(context, task_method = :task, opts = {})
if defined?(Capistrano) && context.is_a?(Capistrano::Configuration)
context_name = "capistrano"
role_default = "{:except => {:no_release => true}}"
error_type = ::Capistrano::CommandError
else
context_name = "vlad"
role_default = "[:app]"
error_type = ::Rake::CommandFailedError
end
roles = context.fetch(:bundle_roles, false)
opts[:roles] = roles if roles
context.send :namespace, :bundle do
send :desc, <<-DESC
Install the current Bundler environment. By default, gems will be \
installed to the shared/bundle path. Gems in the development and \
test group will not be installed. The install command is executed \
with the --deployment and --quiet flags. If the bundle cmd cannot \
be found then you can override the bundle_cmd variable to specify \
which one it should use. The base path to the app is fetched from \
the :latest_release variable. Set it for custom deploy layouts.
You can override any of these defaults by setting the variables shown below.
N.B. bundle_roles must be defined before you require 'bundler/#{context_name}' \
in your deploy.rb file.
set :bundle_gemfile, "Gemfile"
set :bundle_dir, File.join(fetch(:shared_path), 'bundle')
set :bundle_flags, "--deployment --quiet"
set :bundle_without, [:development, :test]
set :bundle_with, [:mysql]
set :bundle_cmd, "bundle" # e.g. "/opt/ruby/bin/bundle"
set :bundle_roles, #{role_default} # e.g. [:app, :batch]
DESC
send task_method, :install, opts do
bundle_cmd = context.fetch(:bundle_cmd, "bundle")
bundle_flags = context.fetch(:bundle_flags, "--deployment --quiet")
bundle_dir = context.fetch(:bundle_dir, File.join(context.fetch(:shared_path), "bundle"))
bundle_gemfile = context.fetch(:bundle_gemfile, "Gemfile")
bundle_without = [*context.fetch(:bundle_without, [:development, :test])].compact
bundle_with = [*context.fetch(:bundle_with, [])].compact
app_path = context.fetch(:latest_release)
if app_path.to_s.empty?
raise error_type.new("Cannot detect current release path - make sure you have deployed at least once.")
end
args = ["--gemfile #{File.join(app_path, bundle_gemfile)}"]
args << "--path #{bundle_dir}" unless bundle_dir.to_s.empty?
args << bundle_flags.to_s
args << "--without #{bundle_without.join(" ")}" unless bundle_without.empty?
args << "--with #{bundle_with.join(" ")}" unless bundle_with.empty?
run "cd #{app_path} && #{bundle_cmd} install #{args.join(" ")}"
end
end
end
end
end

View File

@@ -1,44 +0,0 @@
# frozen_string_literal: true
begin
require "rubygems/deprecate"
rescue LoadError
# it's fine if it doesn't exist on the current RubyGems...
nil
end
module Bundler
# If Bundler::Deprecate is an autoload constant, we need to define it
if defined?(Bundler::Deprecate) && !autoload?(:Deprecate)
# nothing to do!
elsif defined? ::Deprecate
Deprecate = ::Deprecate
elsif defined? Gem::Deprecate
Deprecate = Gem::Deprecate
else
class Deprecate
end
end
unless Deprecate.respond_to?(:skip_during)
def Deprecate.skip_during
original = skip
self.skip = true
yield
ensure
self.skip = original
end
end
unless Deprecate.respond_to?(:skip)
def Deprecate.skip
@skip ||= false
end
end
unless Deprecate.respond_to?(:skip=)
def Deprecate.skip=(skip)
@skip = skip
end
end
end

View File

@@ -1,71 +0,0 @@
# frozen_string_literal: true
# This code was extracted from https://github.com/Solistra/ruby-digest which is under public domain
module Bundler
module Digest
# The initial constant values for the 32-bit constant words A, B, C, D, and
# E, respectively.
SHA1_WORDS = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0].freeze
# The 8-bit field used for bitwise `AND` masking. Defaults to `0xFFFFFFFF`.
SHA1_MASK = 0xFFFFFFFF
class << self
def sha1(string)
unless string.is_a?(String)
raise TypeError, "can't convert #{string.class.inspect} into String"
end
buffer = string.b
words = SHA1_WORDS.dup
generate_split_buffer(buffer) do |chunk|
w = []
chunk.each_slice(4) do |a, b, c, d|
w << (((a << 8 | b) << 8 | c) << 8 | d)
end
a, b, c, d, e = *words
(16..79).each do |i|
w[i] = SHA1_MASK & rotate((w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16]), 1)
end
0.upto(79) do |i|
case i
when 0..19
f = ((b & c) | (~b & d))
k = 0x5A827999
when 20..39
f = (b ^ c ^ d)
k = 0x6ED9EBA1
when 40..59
f = ((b & c) | (b & d) | (c & d))
k = 0x8F1BBCDC
when 60..79
f = (b ^ c ^ d)
k = 0xCA62C1D6
end
t = SHA1_MASK & rotate(a, 5) + f + e + k + w[i]
a, b, c, d, e = t, a, SHA1_MASK & rotate(b, 30), c, d # rubocop:disable Style/ParallelAssignment
end
mutated = [a, b, c, d, e]
words.map!.with_index {|word, index| SHA1_MASK & (word + mutated[index]) }
end
words.pack("N*").unpack("H*").first
end
private
def generate_split_buffer(string, &block)
size = string.bytesize * 8
buffer = string.bytes << 128
buffer << 0 while buffer.size % 64 != 56
buffer.concat([size].pack("Q>").bytes)
buffer.each_slice(64, &block)
end
def rotate(value, spaces)
value << spaces | value >> (32 - spaces)
end
end
end
end

View File

@@ -1,583 +0,0 @@
# frozen_string_literal: true
require_relative "dependency"
require_relative "ruby_dsl"
module Bundler
class Dsl
include RubyDsl
def self.evaluate(gemfile, lockfile, unlock)
builder = new
builder.eval_gemfile(gemfile)
builder.to_definition(lockfile, unlock)
end
VALID_PLATFORMS = Bundler::Dependency::PLATFORM_MAP.keys.freeze
VALID_KEYS = %w[group groups git path glob name branch ref tag require submodules
platform platforms type source install_if gemfile force_ruby_platform].freeze
GITHUB_PULL_REQUEST_URL = %r{\Ahttps://github\.com/([A-Za-z0-9_\-\.]+/[A-Za-z0-9_\-\.]+)/pull/(\d+)\z}.freeze
attr_reader :gemspecs
attr_accessor :dependencies
def initialize
@source = nil
@sources = SourceList.new
@git_sources = {}
@dependencies = []
@groups = []
@install_conditionals = []
@optional_groups = []
@platforms = []
@env = nil
@ruby_version = nil
@gemspecs = []
@gemfile = nil
@gemfiles = []
add_git_sources
end
def eval_gemfile(gemfile, contents = nil)
expanded_gemfile_path = Pathname.new(gemfile).expand_path(@gemfile&.parent)
original_gemfile = @gemfile
@gemfile = expanded_gemfile_path
@gemfiles << expanded_gemfile_path
contents ||= Bundler.read_file(@gemfile.to_s)
instance_eval(contents.dup.tap {|x| x.untaint if RUBY_VERSION < "2.7" }, gemfile.to_s, 1)
rescue Exception => e # rubocop:disable Lint/RescueException
message = "There was an error " \
"#{e.is_a?(GemfileEvalError) ? "evaluating" : "parsing"} " \
"`#{File.basename gemfile.to_s}`: #{e.message}"
raise DSLError.new(message, gemfile, e.backtrace, contents)
ensure
@gemfile = original_gemfile
end
def gemspec(opts = nil)
opts ||= {}
path = opts[:path] || "."
glob = opts[:glob]
name = opts[:name]
development_group = opts[:development_group] || :development
expanded_path = gemfile_root.join(path)
gemspecs = Gem::Util.glob_files_in_dir("{,*}.gemspec", expanded_path).map {|g| Bundler.load_gemspec(g) }.compact
gemspecs.reject! {|s| s.name != name } if name
specs_by_name_and_version = gemspecs.group_by {|s| [s.name, s.version] }
case specs_by_name_and_version.size
when 1
specs = specs_by_name_and_version.values.first
spec = specs.find {|s| s.match_platform(Bundler.local_platform) } || specs.first
@gemspecs << spec
gem spec.name, :name => spec.name, :path => path, :glob => glob
group(development_group) do
spec.development_dependencies.each do |dep|
gem dep.name, *(dep.requirement.as_list + [:type => :development])
end
end
when 0
raise InvalidOption, "There are no gemspecs at #{expanded_path}"
else
raise InvalidOption, "There are multiple gemspecs at #{expanded_path}. " \
"Please use the :name option to specify which one should be used"
end
end
def gem(name, *args)
options = args.last.is_a?(Hash) ? args.pop.dup : {}
options["gemfile"] = @gemfile
version = args || [">= 0"]
normalize_options(name, version, options)
dep = Dependency.new(name, version, options)
# if there's already a dependency with this name we try to prefer one
if current = @dependencies.find {|d| d.name == dep.name }
deleted_dep = @dependencies.delete(current) if current.type == :development
unless deleted_dep
if current.requirement != dep.requirement
return if dep.type == :development
update_prompt = ""
if File.basename(@gemfile) == Injector::INJECTED_GEMS
if dep.requirements_list.include?(">= 0") && !current.requirements_list.include?(">= 0")
update_prompt = ". Gem already added"
else
update_prompt = ". If you want to update the gem version, run `bundle update #{current.name}`"
update_prompt += ". You may also need to change the version requirement specified in the Gemfile if it's too restrictive." unless current.requirements_list.include?(">= 0")
end
end
raise GemfileError, "You cannot specify the same gem twice with different version requirements.\n" \
"You specified: #{current.name} (#{current.requirement}) and #{dep.name} (#{dep.requirement})" \
"#{update_prompt}"
elsif current.source != dep.source
return if dep.type == :development
raise GemfileError, "You cannot specify the same gem twice coming from different sources.\n" \
"You specified that #{dep.name} (#{dep.requirement}) should come from " \
"#{current.source || "an unspecified source"} and #{dep.source}\n"
else
Bundler.ui.warn "Your Gemfile lists the gem #{current.name} (#{current.requirement}) more than once.\n" \
"You should probably keep only one of them.\n" \
"Remove any duplicate entries and specify the gem only once.\n" \
"While it's not a problem now, it could cause errors if you change the version of one of them later."
end
end
end
@dependencies << dep
end
def source(source, *args, &blk)
options = args.last.is_a?(Hash) ? args.pop.dup : {}
options = normalize_hash(options)
source = normalize_source(source)
if options.key?("type")
options["type"] = options["type"].to_s
unless Plugin.source?(options["type"])
raise InvalidOption, "No plugin sources available for #{options["type"]}"
end
unless block_given?
raise InvalidOption, "You need to pass a block to #source with :type option"
end
source_opts = options.merge("uri" => source)
with_source(@sources.add_plugin_source(options["type"], source_opts), &blk)
elsif block_given?
with_source(@sources.add_rubygems_source("remotes" => source), &blk)
else
@sources.add_global_rubygems_remote(source)
end
end
def git_source(name, &block)
unless block_given?
raise InvalidOption, "You need to pass a block to #git_source"
end
if valid_keys.include?(name.to_s)
raise InvalidOption, "You cannot use #{name} as a git source. It " \
"is a reserved key. Reserved keys are: #{valid_keys.join(", ")}"
end
@git_sources[name.to_s] = block
end
def path(path, options = {}, &blk)
source_options = normalize_hash(options).merge(
"path" => Pathname.new(path),
"root_path" => gemfile_root,
"gemspec" => gemspecs.find {|g| g.name == options["name"] }
)
source_options["global"] = true unless block_given?
source = @sources.add_path_source(source_options)
with_source(source, &blk)
end
def git(uri, options = {}, &blk)
unless block_given?
msg = "You can no longer specify a git source by itself. Instead, \n" \
"either use the :git option on a gem, or specify the gems that \n" \
"bundler should find in the git source by passing a block to \n" \
"the git method, like: \n\n" \
" git 'git://github.com/rails/rails.git' do\n" \
" gem 'rails'\n" \
" end"
raise DeprecatedError, msg
end
with_source(@sources.add_git_source(normalize_hash(options).merge("uri" => uri)), &blk)
end
def github(repo, options = {})
raise ArgumentError, "GitHub sources require a block" unless block_given?
github_uri = @git_sources["github"].call(repo)
git_options = normalize_hash(options).merge("uri" => github_uri)
git_source = @sources.add_git_source(git_options)
with_source(git_source) { yield }
end
def to_definition(lockfile, unlock)
check_primary_source_safety
Definition.new(lockfile, @dependencies, @sources, unlock, @ruby_version, @optional_groups, @gemfiles)
end
def group(*args, &blk)
options = args.last.is_a?(Hash) ? args.pop.dup : {}
normalize_group_options(options, args)
@groups.concat args
if options["optional"]
optional_groups = args - @optional_groups
@optional_groups.concat optional_groups
end
yield
ensure
args.each { @groups.pop }
end
def install_if(*args)
@install_conditionals.concat args
yield
ensure
args.each { @install_conditionals.pop }
end
def platforms(*platforms)
@platforms.concat platforms
yield
ensure
platforms.each { @platforms.pop }
end
alias_method :platform, :platforms
def env(name)
old = @env
@env = name
yield
ensure
@env = old
end
def plugin(*args)
# Pass on
end
def method_missing(name, *args)
raise GemfileError, "Undefined local variable or method `#{name}' for Gemfile"
end
def check_primary_source_safety
check_path_source_safety
check_rubygems_source_safety
end
private
def add_git_sources
git_source(:github) do |repo_name|
if repo_name =~ GITHUB_PULL_REQUEST_URL
{
"git" => "https://github.com/#{$1}.git",
"branch" => nil,
"ref" => "refs/pull/#{$2}/head",
"tag" => nil,
}
else
repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
"https://github.com/#{repo_name}.git"
end
end
git_source(:gist) do |repo_name|
"https://gist.github.com/#{repo_name}.git"
end
git_source(:bitbucket) do |repo_name|
user_name, repo_name = repo_name.split("/")
repo_name ||= user_name
"https://#{user_name}@bitbucket.org/#{user_name}/#{repo_name}.git"
end
end
def with_source(source)
old_source = @source
if block_given?
@source = source
yield
end
source
ensure
@source = old_source
end
def normalize_hash(opts)
opts.keys.each do |k|
opts[k.to_s] = opts.delete(k) unless k.is_a?(String)
end
opts
end
def valid_keys
@valid_keys ||= VALID_KEYS
end
def normalize_options(name, version, opts)
if name.is_a?(Symbol)
raise GemfileError, %(You need to specify gem names as Strings. Use 'gem "#{name}"' instead)
end
if /\s/.match?(name)
raise GemfileError, %('#{name}' is not a valid gem name because it contains whitespace)
end
raise GemfileError, %(an empty gem name is not valid) if name.empty?
normalize_hash(opts)
git_names = @git_sources.keys.map(&:to_s)
validate_keys("gem '#{name}'", opts, valid_keys + git_names)
groups = @groups.dup
opts["group"] = opts.delete("groups") || opts["group"]
groups.concat Array(opts.delete("group"))
groups = [:default] if groups.empty?
install_if = @install_conditionals.dup
install_if.concat Array(opts.delete("install_if"))
install_if = install_if.reduce(true) do |memo, val|
memo && (val.respond_to?(:call) ? val.call : val)
end
platforms = @platforms.dup
opts["platforms"] = opts["platform"] || opts["platforms"]
platforms.concat Array(opts.delete("platforms"))
platforms.map!(&:to_sym)
platforms.each do |p|
next if VALID_PLATFORMS.include?(p)
raise GemfileError, "`#{p}` is not a valid platform. The available options are: #{VALID_PLATFORMS.inspect}"
end
# Save sources passed in a key
if opts.key?("source")
source = normalize_source(opts["source"])
opts["source"] = @sources.add_rubygems_source("remotes" => source)
end
git_name = (git_names & opts.keys).last
if @git_sources[git_name]
git_opts = @git_sources[git_name].call(opts[git_name])
git_opts = { "git" => git_opts } if git_opts.is_a?(String)
opts.merge!(git_opts) do |key, _gemfile_value, _git_source_value|
raise GemfileError, %(The :#{key} option can't be used with `#{git_name}: #{opts[git_name].inspect}`)
end
end
%w[git path].each do |type|
next unless param = opts[type]
if version.first && version.first =~ /^\s*=?\s*(\d[^\s]*)\s*$/
options = opts.merge("name" => name, "version" => $1)
else
options = opts.dup
end
source = send(type, param, options) {}
opts["source"] = source
end
opts["source"] ||= @source
opts["env"] ||= @env
opts["platforms"] = platforms.dup
opts["group"] = groups
opts["should_include"] = install_if
end
def normalize_group_options(opts, groups)
normalize_hash(opts)
groups = groups.map {|group| ":#{group}" }.join(", ")
validate_keys("group #{groups}", opts, %w[optional])
opts["optional"] ||= false
end
def validate_keys(command, opts, valid_keys)
invalid_keys = opts.keys - valid_keys
git_source = opts.keys & @git_sources.keys.map(&:to_s)
if opts["branch"] && !(opts["git"] || opts["github"] || git_source.any?)
raise GemfileError, %(The `branch` option for `#{command}` is not allowed. Only gems with a git source can specify a branch)
end
return true unless invalid_keys.any?
message = String.new
message << "You passed #{invalid_keys.map {|k| ":" + k }.join(", ")} "
message << if invalid_keys.size > 1
"as options for #{command}, but they are invalid."
else
"as an option for #{command}, but it is invalid."
end
message << " Valid options are: #{valid_keys.join(", ")}."
message << " You may be able to resolve this by upgrading Bundler to the newest version."
raise InvalidOption, message
end
def normalize_source(source)
case source
when :gemcutter, :rubygems, :rubyforge
Bundler::SharedHelpers.major_deprecation 2, "The source :#{source} is deprecated because HTTP " \
"requests are insecure.\nPlease change your source to 'https://" \
"rubygems.org' if possible, or 'http://rubygems.org' if not."
"http://rubygems.org"
when String
source
else
raise GemfileError, "Unknown source '#{source}'"
end
end
def check_path_source_safety
return if @sources.global_path_source.nil?
msg = "You can no longer specify a path source by itself. Instead, \n" \
"either use the :path option on a gem, or specify the gems that \n" \
"bundler should find in the path source by passing a block to \n" \
"the path method, like: \n\n" \
" path 'dir/containing/rails' do\n" \
" gem 'rails'\n" \
" end\n\n"
SharedHelpers.major_deprecation(2, msg.strip)
end
def check_rubygems_source_safety
if @sources.implicit_global_source?
implicit_global_source_warning
elsif @sources.aggregate_global_source?
multiple_global_source_warning
end
end
def implicit_global_source_warning
Bundler::SharedHelpers.major_deprecation 2, "This Gemfile does not include an explicit global source. " \
"Not using an explicit global source may result in a different lockfile being generated depending on " \
"the gems you have installed locally before bundler is run. " \
"Instead, define a global source in your Gemfile like this: source \"https://rubygems.org\"."
end
def multiple_global_source_warning
if Bundler.feature_flag.bundler_3_mode?
msg = "This Gemfile contains multiple global sources. " \
"Each source after the first must include a block to indicate which gems " \
"should come from that source"
raise GemfileEvalError, msg
else
Bundler::SharedHelpers.major_deprecation 2, "Your Gemfile contains multiple global sources. " \
"Using `source` more than once without a block is a security risk, and " \
"may result in installing unexpected gems. To resolve this warning, use " \
"a block to indicate which gems should come from the secondary source."
end
end
class DSLError < GemfileError
# @return [String] the description that should be presented to the user.
#
attr_reader :description
# @return [String] the path of the dsl file that raised the exception.
#
attr_reader :dsl_path
# @return [Exception] the backtrace of the exception raised by the
# evaluation of the dsl file.
#
attr_reader :backtrace
# @param [Exception] backtrace @see backtrace
# @param [String] dsl_path @see dsl_path
#
def initialize(description, dsl_path, backtrace, contents = nil)
@status_code = $!.respond_to?(:status_code) && $!.status_code
@description = description
@dsl_path = dsl_path
@backtrace = backtrace
@contents = contents
end
def status_code
@status_code || super
end
# @return [String] the contents of the DSL that cause the exception to
# be raised.
#
def contents
@contents ||= dsl_path && File.exist?(dsl_path) && File.read(dsl_path)
end
# The message of the exception reports the content of podspec for the
# line that generated the original exception.
#
# @example Output
#
# Invalid podspec at `RestKit.podspec` - undefined method
# `exclude_header_search_paths=' for #<Pod::Specification for
# `RestKit/Network (0.9.3)`>
#
# from spec-repos/master/RestKit/0.9.3/RestKit.podspec:36
# -------------------------------------------
# # because it would break: #import <CoreData/CoreData.h>
# > ns.exclude_header_search_paths = 'Code/RestKit.h'
# end
# -------------------------------------------
#
# @return [String] the message of the exception.
#
def to_s
@to_s ||= begin
trace_line, description = parse_line_number_from_description
m = String.new("\n[!] ")
m << description
m << ". Bundler cannot continue.\n"
return m unless backtrace && dsl_path && contents
trace_line = backtrace.find {|l| l.include?(dsl_path.to_s) } || trace_line
return m unless trace_line
line_numer = trace_line.split(":")[1].to_i - 1
return m unless line_numer
lines = contents.lines.to_a
indent = " # "
indicator = indent.tr("#", ">")
first_line = line_numer.zero?
last_line = (line_numer == (lines.count - 1))
m << "\n"
m << "#{indent}from #{trace_line.gsub(/:in.*$/, "")}\n"
m << "#{indent}-------------------------------------------\n"
m << "#{indent}#{lines[line_numer - 1]}" unless first_line
m << "#{indicator}#{lines[line_numer]}"
m << "#{indent}#{lines[line_numer + 1]}" unless last_line
m << "\n" unless m.end_with?("\n")
m << "#{indent}-------------------------------------------\n"
end
end
private
def parse_line_number_from_description
description = self.description
if dsl_path && description =~ /((#{Regexp.quote File.expand_path(dsl_path)}|#{Regexp.quote dsl_path.to_s}):\d+)/
trace_line = Regexp.last_match[1]
description = description.sub(/\n.*\n(\.\.\.)? *\^~+$/, "").sub(/#{Regexp.quote trace_line}:\s*/, "").sub("\n", " - ")
end
[trace_line, description]
end
end
def gemfile_root
@gemfile ||= Bundler.default_gemfile
@gemfile.dirname
end
end
end

View File

@@ -1,143 +0,0 @@
# frozen_string_literal: true
module Bundler
# used for Creating Specifications from the Gemcutter Endpoint
class EndpointSpecification < Gem::Specification
include MatchRemoteMetadata
attr_reader :name, :version, :platform, :checksum
attr_accessor :source, :remote, :dependencies
def initialize(name, version, platform, spec_fetcher, dependencies, metadata = nil)
super()
@name = name
@version = Gem::Version.create version
@platform = Gem::Platform.new(platform)
@spec_fetcher = spec_fetcher
@dependencies = dependencies.map {|dep, reqs| build_dependency(dep, reqs) }
@loaded_from = nil
@remote_specification = nil
parse_metadata(metadata)
end
def fetch_platform
@platform
end
# needed for standalone, load required_paths from local gemspec
# after the gem is installed
def require_paths
if @remote_specification
@remote_specification.require_paths
elsif _local_specification
_local_specification.require_paths
else
super
end
end
# needed for inline
def load_paths
# remote specs aren't installed, and can't have load_paths
if _local_specification
_local_specification.load_paths
else
super
end
end
# needed for binstubs
def executables
if @remote_specification
@remote_specification.executables
elsif _local_specification
_local_specification.executables
else
super
end
end
# needed for bundle clean
def bindir
if @remote_specification
@remote_specification.bindir
elsif _local_specification
_local_specification.bindir
else
super
end
end
# needed for post_install_messages during install
def post_install_message
if @remote_specification
@remote_specification.post_install_message
elsif _local_specification
_local_specification.post_install_message
else
super
end
end
# needed for "with native extensions" during install
def extensions
if @remote_specification
@remote_specification.extensions
elsif _local_specification
_local_specification.extensions
else
super
end
end
def _local_specification
return unless @loaded_from && File.exist?(local_specification_path)
eval(File.read(local_specification_path)).tap do |spec|
spec.loaded_from = @loaded_from
end
end
def __swap__(spec)
SharedHelpers.ensure_same_dependencies(self, dependencies, spec.dependencies)
@remote_specification = spec
end
private
def _remote_specification
@_remote_specification ||= @spec_fetcher.fetch_spec([@name, @version, @platform])
end
def local_specification_path
"#{base_dir}/specifications/#{full_name}.gemspec"
end
def parse_metadata(data)
unless data
@required_ruby_version = nil
@required_rubygems_version = nil
return
end
data.each do |k, v|
next unless v
case k.to_s
when "checksum"
@checksum = v.last
when "rubygems"
@required_rubygems_version = Gem::Requirement.new(v)
when "ruby"
@required_ruby_version = Gem::Requirement.new(v)
end
end
rescue StandardError => e
raise GemspecError, "There was an error parsing the metadata for the gem #{name} (#{version}): #{e.class}\n#{e}\nThe metadata was #{data.inspect}"
end
def build_dependency(name, requirements)
Gem::Dependency.new(name, requirements)
end
end
end

View File

@@ -1,150 +0,0 @@
# frozen_string_literal: true
require_relative "rubygems_integration"
require_relative "source/git/git_proxy"
module Bundler
class Env
def self.write(io)
io.write report
end
def self.report(options = {})
print_gemfile = options.delete(:print_gemfile) { true }
print_gemspecs = options.delete(:print_gemspecs) { true }
out = String.new
append_formatted_table("Environment", environment, out)
append_formatted_table("Bundler Build Metadata", BuildMetadata.to_h, out)
unless Bundler.settings.all.empty?
out << "\n## Bundler settings\n\n```\n"
Bundler.settings.all.each do |setting|
out << setting << "\n"
Bundler.settings.pretty_values_for(setting).each do |line|
out << " " << line << "\n"
end
end
out << "```\n"
end
return out unless SharedHelpers.in_bundle?
if print_gemfile
gemfiles = [Bundler.default_gemfile]
begin
gemfiles = Bundler.definition.gemfiles
rescue GemfileNotFound
nil
end
out << "\n## Gemfile\n"
gemfiles.each do |gemfile|
out << "\n### #{Pathname.new(gemfile).relative_path_from(SharedHelpers.pwd)}\n\n"
out << "```ruby\n" << read_file(gemfile).chomp << "\n```\n"
end
out << "\n### #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)}\n\n"
out << "```\n" << read_file(Bundler.default_lockfile).chomp << "\n```\n"
end
if print_gemspecs
dsl = Dsl.new.tap {|d| d.eval_gemfile(Bundler.default_gemfile) }
out << "\n## Gemspecs\n" unless dsl.gemspecs.empty?
dsl.gemspecs.each do |gs|
out << "\n### #{File.basename(gs.loaded_from)}"
out << "\n\n```ruby\n" << read_file(gs.loaded_from).chomp << "\n```\n"
end
end
out
end
def self.read_file(filename)
Bundler.read_file(filename.to_s).strip
rescue Errno::ENOENT
"<No #{filename} found>"
rescue RuntimeError => e
"#{e.class}: #{e.message}"
end
def self.ruby_version
str = String.new(RUBY_VERSION)
str << "p#{RUBY_PATCHLEVEL}" if defined? RUBY_PATCHLEVEL
str << " (#{RUBY_RELEASE_DATE} revision #{RUBY_REVISION}) [#{Gem::Platform.local}]"
end
def self.git_version
Bundler::Source::Git::GitProxy.new(nil, nil).full_version
rescue Bundler::Source::Git::GitNotInstalledError
"not installed"
end
def self.version_of(script)
return "not installed" unless Bundler.which(script)
`#{script} --version`.chomp
end
def self.chruby_version
return "not installed" unless Bundler.which("chruby-exec")
`chruby-exec -- chruby --version`.
sub(/.*^chruby: (#{Gem::Version::VERSION_PATTERN}).*/m, '\1')
end
def self.environment
out = []
out << ["Bundler", Bundler::VERSION]
out << [" Platforms", Gem.platforms.join(", ")]
out << ["Ruby", ruby_version]
out << [" Full Path", Gem.ruby]
out << [" Config Dir", Pathname.new(Gem::ConfigFile::SYSTEM_WIDE_CONFIG_FILE).dirname]
out << ["RubyGems", Gem::VERSION]
out << [" Gem Home", Gem.dir]
out << [" Gem Path", Gem.path.join(File::PATH_SEPARATOR)]
out << [" User Home", Gem.user_home]
out << [" User Path", Gem.user_dir]
out << [" Bin Dir", Gem.bindir]
if defined?(OpenSSL::SSL)
out << ["OpenSSL"]
out << [" Compiled", OpenSSL::OPENSSL_VERSION] if defined?(OpenSSL::OPENSSL_VERSION)
out << [" Loaded", OpenSSL::OPENSSL_LIBRARY_VERSION] if defined?(OpenSSL::OPENSSL_LIBRARY_VERSION)
out << [" Cert File", OpenSSL::X509::DEFAULT_CERT_FILE] if defined?(OpenSSL::X509::DEFAULT_CERT_FILE)
out << [" Cert Dir", OpenSSL::X509::DEFAULT_CERT_DIR] if defined?(OpenSSL::X509::DEFAULT_CERT_DIR)
end
out << ["Tools"]
out << [" Git", git_version]
out << [" RVM", ENV.fetch("rvm_version") { version_of("rvm") }]
out << [" rbenv", version_of("rbenv")]
out << [" chruby", chruby_version]
%w[rubygems-bundler open_gem].each do |name|
specs = Bundler.rubygems.find_name(name)
out << [" #{name}", "(#{specs.map(&:version).join(",")})"] unless specs.empty?
end
if (exe = caller.last.split(":").first)&.match? %r{(exe|bin)/bundler?\z}
shebang = File.read(exe).lines.first
shebang.sub!(/^#!\s*/, "")
unless shebang.start_with?(Gem.ruby, "/usr/bin/env ruby")
out << ["Gem.ruby", Gem.ruby]
out << ["bundle #!", shebang]
end
end
out
end
def self.append_formatted_table(title, pairs, out)
return if pairs.empty?
out << "\n" unless out.empty?
out << "## #{title}\n\n```\n"
ljust = pairs.map {|k, _v| k.to_s.length }.max
pairs.each do |k, v|
out << "#{k.to_s.ljust(ljust)} #{v}\n"
end
out << "```\n"
end
private_class_method :read_file, :ruby_version, :git_version, :append_formatted_table, :version_of, :chruby_version
end
end

View File

@@ -1,86 +0,0 @@
# frozen_string_literal: true
module Bundler
class EnvironmentPreserver
INTENTIONALLY_NIL = "BUNDLER_ENVIRONMENT_PRESERVER_INTENTIONALLY_NIL"
BUNDLER_KEYS = %w[
BUNDLE_BIN_PATH
BUNDLE_GEMFILE
BUNDLER_VERSION
BUNDLER_SETUP
GEM_HOME
GEM_PATH
MANPATH
PATH
RB_USER_INSTALL
RUBYLIB
RUBYOPT
].map(&:freeze).freeze
BUNDLER_PREFIX = "BUNDLER_ORIG_"
def self.from_env
new(env_to_hash(ENV), BUNDLER_KEYS)
end
def self.env_to_hash(env)
to_hash = env.to_hash
return to_hash unless Gem.win_platform?
to_hash.each_with_object({}) {|(k,v), a| a[k.upcase] = v }
end
# @param env [Hash]
# @param keys [Array<String>]
def initialize(env, keys)
@original = env
@keys = keys
@prefix = BUNDLER_PREFIX
end
# Replaces `ENV` with the bundler environment variables backed up
def replace_with_backup
unless Gem.win_platform?
ENV.replace(backup)
return
end
# Fallback logic for Windows below to workaround
# https://bugs.ruby-lang.org/issues/16798. Can be dropped once all
# supported rubies include the fix for that.
ENV.clear
backup.each {|k, v| ENV[k] = v }
end
# @return [Hash]
def backup
env = @original.clone
@keys.each do |key|
value = env[key]
if !value.nil? && !value.empty?
env[@prefix + key] ||= value
elsif value.nil?
env[@prefix + key] ||= INTENTIONALLY_NIL
end
end
env
end
# @return [Hash]
def restore
env = @original.clone
@keys.each do |key|
value_original = env[@prefix + key]
next if value_original.nil? || value_original.empty?
if value_original == INTENTIONALLY_NIL
env.delete(key)
else
env[key] = value_original
end
env.delete(@prefix + key)
end
env
end
end
end

View File

@@ -1,175 +0,0 @@
# frozen_string_literal: true
module Bundler
class BundlerError < StandardError
def self.status_code(code)
define_method(:status_code) { code }
if match = BundlerError.all_errors.find {|_k, v| v == code }
error, _ = match
raise ArgumentError,
"Trying to register #{self} for status code #{code} but #{error} is already registered"
end
BundlerError.all_errors[self] = code
end
def self.all_errors
@all_errors ||= {}
end
end
class GemfileError < BundlerError; status_code(4); end
class InstallError < BundlerError; status_code(5); end
# Internal error, should be rescued
class SolveFailure < BundlerError; status_code(6); end
class GemNotFound < BundlerError; status_code(7); end
class InstallHookError < BundlerError; status_code(8); end
class GemfileNotFound < BundlerError; status_code(10); end
class GitError < BundlerError; status_code(11); end
class DeprecatedError < BundlerError; status_code(12); end
class PathError < BundlerError; status_code(13); end
class GemspecError < BundlerError; status_code(14); end
class InvalidOption < BundlerError; status_code(15); end
class ProductionError < BundlerError; status_code(16); end
class HTTPError < BundlerError
status_code(17)
def filter_uri(uri)
URICredentialsFilter.credential_filtered_uri(uri)
end
end
class RubyVersionMismatch < BundlerError; status_code(18); end
class SecurityError < BundlerError; status_code(19); end
class LockfileError < BundlerError; status_code(20); end
class CyclicDependencyError < BundlerError; status_code(21); end
class GemfileLockNotFound < BundlerError; status_code(22); end
class PluginError < BundlerError; status_code(29); end
class ThreadCreationError < BundlerError; status_code(33); end
class APIResponseMismatchError < BundlerError; status_code(34); end
class APIResponseInvalidDependenciesError < BundlerError; status_code(35); end
class GemfileEvalError < GemfileError; end
class MarshalError < StandardError; end
class PermissionError < BundlerError
def initialize(path, permission_type = :write)
@path = path
@permission_type = permission_type
end
def action
case @permission_type
when :read then "read from"
when :write then "write to"
when :executable, :exec then "execute"
else @permission_type.to_s
end
end
def permission_type
case @permission_type
when :create
"executable permissions for all parent directories and write permissions for `#{parent_folder}`"
else
"#{@permission_type} permissions for that path"
end
end
def parent_folder
File.dirname(@path)
end
def message
"There was an error while trying to #{action} `#{@path}`. " \
"It is likely that you need to grant #{permission_type}."
end
status_code(23)
end
class GemRequireError < BundlerError
attr_reader :orig_exception
def initialize(orig_exception, msg)
full_message = msg + "\nGem Load Error is: #{orig_exception.message}\n"\
"Backtrace for gem load error is:\n"\
"#{orig_exception.backtrace.join("\n")}\n"\
"Bundler Error Backtrace:\n"
super(full_message)
@orig_exception = orig_exception
end
status_code(24)
end
class YamlSyntaxError < BundlerError
attr_reader :orig_exception
def initialize(orig_exception, msg)
super(msg)
@orig_exception = orig_exception
end
status_code(25)
end
class TemporaryResourceError < PermissionError
def message
"There was an error while trying to #{action} `#{@path}`. " \
"Some resource was temporarily unavailable. It's suggested that you try" \
"the operation again."
end
status_code(26)
end
class VirtualProtocolError < BundlerError
def message
"There was an error relating to virtualization and file access. " \
"It is likely that you need to grant access to or mount some file system correctly."
end
status_code(27)
end
class OperationNotSupportedError < PermissionError
def message
"Attempting to #{action} `#{@path}` is unsupported by your OS."
end
status_code(28)
end
class NoSpaceOnDeviceError < PermissionError
def message
"There was an error while trying to #{action} `#{@path}`. " \
"There was insufficient space remaining on the device."
end
status_code(31)
end
class GenericSystemCallError < BundlerError
attr_reader :underlying_error
def initialize(underlying_error, message)
@underlying_error = underlying_error
super("#{message}\nThe underlying system error is #{@underlying_error.class}: #{@underlying_error}")
end
status_code(32)
end
class DirectoryRemovalError < BundlerError
def initialize(orig_exception, msg)
full_message = "#{msg}.\n" \
"The underlying error was #{orig_exception.class}: #{orig_exception.message}, with backtrace:\n" \
" #{orig_exception.backtrace.join("\n ")}\n\n" \
"Bundler Error Backtrace:"
super(full_message)
end
status_code(36)
end
end

View File

@@ -1,53 +0,0 @@
# frozen_string_literal: true
module Bundler
class FeatureFlag
def self.settings_flag(flag, &default)
unless Bundler::Settings::BOOL_KEYS.include?(flag.to_s)
raise "Cannot use `#{flag}` as a settings feature flag since it isn't a bool key"
end
settings_method("#{flag}?", flag, &default)
end
private_class_method :settings_flag
def self.settings_option(key, &default)
settings_method(key, key, &default)
end
private_class_method :settings_option
def self.settings_method(name, key, &default)
define_method(name) do
value = Bundler.settings[key]
value = instance_eval(&default) if value.nil?
value
end
end
private_class_method :settings_method
(1..10).each {|v| define_method("bundler_#{v}_mode?") { major_version >= v } }
settings_flag(:allow_offline_install) { bundler_3_mode? }
settings_flag(:auto_clean_without_path) { bundler_3_mode? }
settings_flag(:cache_all) { bundler_3_mode? }
settings_flag(:default_install_uses_path) { bundler_3_mode? }
settings_flag(:forget_cli_options) { bundler_3_mode? }
settings_flag(:global_gem_cache) { bundler_3_mode? }
settings_flag(:path_relative_to_cwd) { bundler_3_mode? }
settings_flag(:plugins) { @bundler_version >= Gem::Version.new("1.14") }
settings_flag(:print_only_version_number) { bundler_3_mode? }
settings_flag(:setup_makes_kernel_gem_public) { !bundler_3_mode? }
settings_flag(:update_requires_all_flag) { bundler_4_mode? }
settings_option(:default_cli_command) { bundler_3_mode? ? :cli_help : :install }
def initialize(bundler_version)
@bundler_version = Gem::Version.create(bundler_version)
end
def major_version
@bundler_version.segments.first
end
private :major_version
end
end

View File

@@ -1,320 +0,0 @@
# frozen_string_literal: true
require_relative "vendored_persistent"
require "cgi"
require "securerandom"
require "zlib"
require "rubygems/request"
module Bundler
# Handles all the fetching with the rubygems server
class Fetcher
autoload :CompactIndex, File.expand_path("fetcher/compact_index", __dir__)
autoload :Downloader, File.expand_path("fetcher/downloader", __dir__)
autoload :Dependency, File.expand_path("fetcher/dependency", __dir__)
autoload :Index, File.expand_path("fetcher/index", __dir__)
# This error is raised when it looks like the network is down
class NetworkDownError < HTTPError; end
# This error is raised if we should rate limit our requests to the API
class TooManyRequestsError < HTTPError; end
# This error is raised if the API returns a 413 (only printed in verbose)
class FallbackError < HTTPError; end
# This is the error raised if OpenSSL fails the cert verification
class CertificateFailureError < HTTPError
def initialize(remote_uri)
remote_uri = filter_uri(remote_uri)
super "Could not verify the SSL certificate for #{remote_uri}.\nThere" \
" is a chance you are experiencing a man-in-the-middle attack, but" \
" most likely your system doesn't have the CA certificates needed" \
" for verification. For information about OpenSSL certificates, see" \
" https://railsapps.github.io/openssl-certificate-verify-failed.html."
end
end
# This is the error raised when a source is HTTPS and OpenSSL didn't load
class SSLError < HTTPError
def initialize(msg = nil)
super msg || "Could not load OpenSSL.\n" \
"You must recompile Ruby with OpenSSL support."
end
end
# This error is raised if HTTP authentication is required, but not provided.
class AuthenticationRequiredError < HTTPError
def initialize(remote_uri)
remote_uri = filter_uri(remote_uri)
super "Authentication is required for #{remote_uri}.\n" \
"Please supply credentials for this source. You can do this by running:\n" \
"`bundle config set --global #{remote_uri} username:password`\n" \
"or by storing the credentials in the `#{Settings.key_for(remote_uri)}` environment variable"
end
end
# This error is raised if HTTP authentication is provided, but incorrect.
class BadAuthenticationError < HTTPError
def initialize(remote_uri)
remote_uri = filter_uri(remote_uri)
super "Bad username or password for #{remote_uri}.\n" \
"Please double-check your credentials and correct them."
end
end
# This error is raised if HTTP authentication is correct, but lacks
# necessary permissions.
class AuthenticationForbiddenError < HTTPError
def initialize(remote_uri)
remote_uri = filter_uri(remote_uri)
super "Access token could not be authenticated for #{remote_uri}.\n" \
"Make sure it's valid and has the necessary scopes configured."
end
end
# Exceptions classes that should bypass retry attempts. If your password didn't work the
# first time, it's not going to the third time.
NET_ERRORS = [:HTTPBadGateway, :HTTPBadRequest, :HTTPFailedDependency,
:HTTPForbidden, :HTTPInsufficientStorage, :HTTPMethodNotAllowed,
:HTTPMovedPermanently, :HTTPNoContent, :HTTPNotFound,
:HTTPNotImplemented, :HTTPPreconditionFailed, :HTTPRequestEntityTooLarge,
:HTTPRequestURITooLong, :HTTPUnauthorized, :HTTPUnprocessableEntity,
:HTTPUnsupportedMediaType, :HTTPVersionNotSupported].freeze
FAIL_ERRORS = begin
fail_errors = [AuthenticationRequiredError, BadAuthenticationError, AuthenticationForbiddenError, FallbackError]
fail_errors << Gem::Requirement::BadRequirementError
fail_errors.concat(NET_ERRORS.map {|e| Net.const_get(e) })
end.freeze
class << self
attr_accessor :disable_endpoint, :api_timeout, :redirect_limit, :max_retries
end
self.redirect_limit = Bundler.settings[:redirect] # How many redirects to allow in one request
self.api_timeout = Bundler.settings[:timeout] # How long to wait for each API call
self.max_retries = Bundler.settings[:retry] # How many retries for the API call
def initialize(remote)
@remote = remote
Socket.do_not_reverse_lookup = true
connection # create persistent connection
end
def uri
@remote.anonymized_uri
end
# fetch a gem specification
def fetch_spec(spec)
spec -= [nil, "ruby", ""]
spec_file_name = "#{spec.join "-"}.gemspec"
uri = Bundler::URI.parse("#{remote_uri}#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}.rz")
if uri.scheme == "file"
path = Bundler.rubygems.correct_for_windows_path(uri.path)
Bundler.safe_load_marshal Bundler.rubygems.inflate(Gem.read_binary(path))
elsif cached_spec_path = gemspec_cached_path(spec_file_name)
Bundler.load_gemspec(cached_spec_path)
else
Bundler.safe_load_marshal Bundler.rubygems.inflate(downloader.fetch(uri).body)
end
rescue MarshalError
raise HTTPError, "Gemspec #{spec} contained invalid data.\n" \
"Your network or your gem server is probably having issues right now."
end
# return the specs in the bundler format as an index with retries
def specs_with_retry(gem_names, source)
Bundler::Retry.new("fetcher", FAIL_ERRORS).attempts do
specs(gem_names, source)
end
end
# return the specs in the bundler format as an index
def specs(gem_names, source)
index = Bundler::Index.new
if Bundler::Fetcher.disable_endpoint
@use_api = false
specs = fetchers.last.specs(gem_names)
else
specs = []
@fetchers = fetchers.drop_while do |f|
!f.available? || (f.api_fetcher? && !gem_names) || !specs = f.specs(gem_names)
end
@use_api = false if fetchers.none?(&:api_fetcher?)
end
specs.each do |name, version, platform, dependencies, metadata|
spec = if dependencies
EndpointSpecification.new(name, version, platform, self, dependencies, metadata)
else
RemoteSpecification.new(name, version, platform, self)
end
spec.source = source
spec.remote = @remote
index << spec
end
index
rescue CertificateFailureError
Bundler.ui.info "" if gem_names && use_api # newline after dots
raise
end
def use_api
return @use_api if defined?(@use_api)
fetchers.shift until fetchers.first.available?
@use_api = if remote_uri.scheme == "file" || Bundler::Fetcher.disable_endpoint
false
else
fetchers.first.api_fetcher?
end
end
def user_agent
@user_agent ||= begin
ruby = Bundler::RubyVersion.system
agent = String.new("bundler/#{Bundler::VERSION}")
agent << " rubygems/#{Gem::VERSION}"
agent << " ruby/#{ruby.versions_string(ruby.versions)}"
agent << " (#{ruby.host})"
agent << " command/#{ARGV.first}"
if ruby.engine != "ruby"
# engine_version raises on unknown engines
engine_version = begin
ruby.engine_versions
rescue RuntimeError
"???"
end
agent << " #{ruby.engine}/#{ruby.versions_string(engine_version)}"
end
agent << " options/#{Bundler.settings.all.join(",")}"
agent << " ci/#{cis.join(",")}" if cis.any?
# add a random ID so we can consolidate runs server-side
agent << " " << SecureRandom.hex(8)
# add any user agent strings set in the config
extra_ua = Bundler.settings[:user_agent]
agent << " " << extra_ua if extra_ua
agent
end
end
def fetchers
@fetchers ||= FETCHERS.map {|f| f.new(downloader, @remote, uri) }
end
def http_proxy
return unless uri = connection.proxy_uri
uri.to_s
end
def inspect
"#<#{self.class}:0x#{object_id} uri=#{uri}>"
end
private
FETCHERS = [CompactIndex, Dependency, Index].freeze
def cis
env_cis = {
"TRAVIS" => "travis",
"CIRCLECI" => "circle",
"SEMAPHORE" => "semaphore",
"JENKINS_URL" => "jenkins",
"BUILDBOX" => "buildbox",
"GO_SERVER_URL" => "go",
"SNAP_CI" => "snap",
"GITLAB_CI" => "gitlab",
"GITHUB_ACTIONS" => "github",
"CI_NAME" => ENV["CI_NAME"],
"CI" => "ci",
}
env_cis.find_all {|env, _| ENV[env] }.map {|_, ci| ci }
end
def connection
@connection ||= begin
needs_ssl = remote_uri.scheme == "https" ||
Bundler.settings[:ssl_verify_mode] ||
Bundler.settings[:ssl_client_cert]
raise SSLError if needs_ssl && !defined?(OpenSSL::SSL)
con = PersistentHTTP.new :name => "bundler", :proxy => :ENV
if gem_proxy = Gem.configuration[:http_proxy]
con.proxy = Bundler::URI.parse(gem_proxy) if gem_proxy != :no_proxy
end
if remote_uri.scheme == "https"
con.verify_mode = (Bundler.settings[:ssl_verify_mode] ||
OpenSSL::SSL::VERIFY_PEER)
con.cert_store = bundler_cert_store
end
ssl_client_cert = Bundler.settings[:ssl_client_cert] ||
(Gem.configuration.ssl_client_cert if
Gem.configuration.respond_to?(:ssl_client_cert))
if ssl_client_cert
pem = File.read(ssl_client_cert)
con.cert = OpenSSL::X509::Certificate.new(pem)
con.key = OpenSSL::PKey::RSA.new(pem)
end
con.read_timeout = Fetcher.api_timeout
con.open_timeout = Fetcher.api_timeout
con.override_headers["User-Agent"] = user_agent
con.override_headers["X-Gemfile-Source"] = @remote.original_uri.to_s if @remote.original_uri
con
end
end
# cached gem specification path, if one exists
def gemspec_cached_path(spec_file_name)
paths = Bundler.rubygems.spec_cache_dirs.map {|dir| File.join(dir, spec_file_name) }
paths.find {|path| File.file? path }
end
HTTP_ERRORS = [
Timeout::Error, EOFError, SocketError, Errno::ENETDOWN, Errno::ENETUNREACH,
Errno::EINVAL, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::EAGAIN,
Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError,
PersistentHTTP::Error, Zlib::BufError, Errno::EHOSTUNREACH
].freeze
def bundler_cert_store
store = OpenSSL::X509::Store.new
ssl_ca_cert = Bundler.settings[:ssl_ca_cert] ||
(Gem.configuration.ssl_ca_cert if
Gem.configuration.respond_to?(:ssl_ca_cert))
if ssl_ca_cert
if File.directory? ssl_ca_cert
store.add_path ssl_ca_cert
else
store.add_file ssl_ca_cert
end
else
store.set_default_paths
Gem::Request.get_cert_files.each {|c| store.add_file c }
end
store
end
def remote_uri
@remote.uri
end
def downloader
@downloader ||= Downloader.new(connection, self.class.redirect_limit)
end
end
end

View File

@@ -1,50 +0,0 @@
# frozen_string_literal: true
module Bundler
class Fetcher
class Base
attr_reader :downloader
attr_reader :display_uri
attr_reader :remote
def initialize(downloader, remote, display_uri)
raise "Abstract class" if self.class == Base
@downloader = downloader
@remote = remote
@display_uri = display_uri
end
def remote_uri
@remote.uri
end
def fetch_uri
@fetch_uri ||= if remote_uri.host == "rubygems.org"
uri = remote_uri.dup
uri.host = "index.rubygems.org"
uri
else
remote_uri
end
end
def available?
true
end
def api_fetcher?
false
end
private
def log_specs(debug_msg)
if Bundler.ui.debug?
Bundler.ui.debug debug_msg
else
Bundler.ui.info ".", false
end
end
end
end
end

View File

@@ -1,133 +0,0 @@
# frozen_string_literal: true
require_relative "base"
require_relative "../worker"
module Bundler
autoload :CompactIndexClient, File.expand_path("../compact_index_client", __dir__)
class Fetcher
class CompactIndex < Base
def self.compact_index_request(method_name)
method = instance_method(method_name)
undef_method(method_name)
define_method(method_name) do |*args, &blk|
method.bind(self).call(*args, &blk)
rescue NetworkDownError, CompactIndexClient::Updater::MisMatchedChecksumError => e
raise HTTPError, e.message
rescue AuthenticationRequiredError, BadAuthenticationError
# Fail since we got a 401 from the server.
raise
rescue HTTPError => e
Bundler.ui.trace(e)
nil
end
end
def specs(gem_names)
specs_for_names(gem_names)
end
compact_index_request :specs
def specs_for_names(gem_names)
gem_info = []
complete_gems = []
remaining_gems = gem_names.dup
until remaining_gems.empty?
log_specs "Looking up gems #{remaining_gems.inspect}"
deps = begin
parallel_compact_index_client.dependencies(remaining_gems)
rescue TooManyRequestsError
@bundle_worker&.stop
@bundle_worker = nil # reset it. Not sure if necessary
serial_compact_index_client.dependencies(remaining_gems)
end
next_gems = deps.map {|d| d[3].map(&:first).flatten(1) }.flatten(1).uniq
deps.each {|dep| gem_info << dep }
complete_gems.concat(deps.map(&:first)).uniq!
remaining_gems = next_gems - complete_gems
end
@bundle_worker&.stop
@bundle_worker = nil # reset it. Not sure if necessary
gem_info
end
def available?
unless SharedHelpers.md5_available?
Bundler.ui.debug("FIPS mode is enabled, bundler can't use the CompactIndex API")
return nil
end
if fetch_uri.scheme == "file"
Bundler.ui.debug("Using a local server, bundler won't use the CompactIndex API")
return false
end
# Read info file checksums out of /versions, so we can know if gems are up to date
compact_index_client.update_and_parse_checksums!
rescue CompactIndexClient::Updater::MisMatchedChecksumError => e
Bundler.ui.debug(e.message)
nil
end
compact_index_request :available?
def api_fetcher?
true
end
private
def compact_index_client
@compact_index_client ||=
SharedHelpers.filesystem_access(cache_path) do
CompactIndexClient.new(cache_path, client_fetcher)
end
end
def parallel_compact_index_client
compact_index_client.execution_mode = lambda do |inputs, &blk|
func = lambda {|object, _index| blk.call(object) }
worker = bundle_worker(func)
inputs.each {|input| worker.enq(input) }
inputs.map { worker.deq }
end
compact_index_client
end
def serial_compact_index_client
compact_index_client.sequential_execution_mode!
compact_index_client
end
def bundle_worker(func = nil)
@bundle_worker ||= begin
worker_name = "Compact Index (#{display_uri.host})"
Bundler::Worker.new(Bundler.settings.processor_count, worker_name, func)
end
@bundle_worker.tap do |worker|
worker.instance_variable_set(:@func, func) if func
end
end
def cache_path
Bundler.user_cache.join("compact_index", remote.cache_slug)
end
def client_fetcher
ClientFetcher.new(self, Bundler.ui)
end
ClientFetcher = Struct.new(:fetcher, :ui) do
def call(path, headers)
fetcher.downloader.fetch(fetcher.fetch_uri + path, headers)
rescue NetworkDownError => e
raise unless Bundler.feature_flag.allow_offline_install? && headers["If-None-Match"]
ui.warn "Using the cached data for the new index because of a network error: #{e}"
Net::HTTPNotModified.new(nil, nil, nil)
end
end
end
end
end

View File

@@ -1,78 +0,0 @@
# frozen_string_literal: true
require_relative "base"
require "cgi"
module Bundler
class Fetcher
class Dependency < Base
def available?
@available ||= fetch_uri.scheme != "file" && downloader.fetch(dependency_api_uri)
rescue NetworkDownError => e
raise HTTPError, e.message
rescue AuthenticationRequiredError
# Fail since we got a 401 from the server.
raise
rescue HTTPError
false
end
def api_fetcher?
true
end
def specs(gem_names, full_dependency_list = [], last_spec_list = [])
query_list = gem_names.uniq - full_dependency_list
log_specs "Query List: #{query_list.inspect}"
return last_spec_list if query_list.empty?
spec_list, deps_list = Bundler::Retry.new("dependency api", FAIL_ERRORS).attempts do
dependency_specs(query_list)
end
returned_gems = spec_list.map(&:first).uniq
specs(deps_list, full_dependency_list + returned_gems, spec_list + last_spec_list)
rescue MarshalError, HTTPError, GemspecError
Bundler.ui.info "" unless Bundler.ui.debug? # new line now that the dots are over
Bundler.ui.debug "could not fetch from the dependency API, trying the full index"
nil
end
def dependency_specs(gem_names)
Bundler.ui.debug "Query Gemcutter Dependency Endpoint API: #{gem_names.join(",")}"
gem_list = unmarshalled_dep_gems(gem_names)
get_formatted_specs_and_deps(gem_list)
end
def unmarshalled_dep_gems(gem_names)
gem_list = []
gem_names.each_slice(Source::Rubygems::API_REQUEST_SIZE) do |names|
marshalled_deps = downloader.fetch(dependency_api_uri(names)).body
gem_list.concat(Bundler.safe_load_marshal(marshalled_deps))
end
gem_list
end
def get_formatted_specs_and_deps(gem_list)
deps_list = []
spec_list = []
gem_list.each do |s|
deps_list.concat(s[:dependencies].map(&:first))
deps = s[:dependencies].map {|n, d| [n, d.split(", ")] }
spec_list.push([s[:name], s[:number], s[:platform], deps])
end
[spec_list, deps_list]
end
def dependency_api_uri(gem_names = [])
uri = fetch_uri + "api/v1/dependencies"
uri.query = "gems=#{CGI.escape(gem_names.sort.join(","))}" if gem_names.any?
uri
end
end
end
end

View File

@@ -1,89 +0,0 @@
# frozen_string_literal: true
module Bundler
class Fetcher
class Downloader
attr_reader :connection
attr_reader :redirect_limit
def initialize(connection, redirect_limit)
@connection = connection
@redirect_limit = redirect_limit
end
def fetch(uri, headers = {}, counter = 0)
raise HTTPError, "Too many redirects" if counter >= redirect_limit
filtered_uri = URICredentialsFilter.credential_filtered_uri(uri)
response = request(uri, headers)
Bundler.ui.debug("HTTP #{response.code} #{response.message} #{filtered_uri}")
case response
when Net::HTTPSuccess, Net::HTTPNotModified
response
when Net::HTTPRedirection
new_uri = Bundler::URI.parse(response["location"])
if new_uri.host == uri.host
new_uri.user = uri.user
new_uri.password = uri.password
end
fetch(new_uri, headers, counter + 1)
when Net::HTTPRequestedRangeNotSatisfiable
new_headers = headers.dup
new_headers.delete("Range")
new_headers["Accept-Encoding"] = "gzip"
fetch(uri, new_headers)
when Net::HTTPRequestEntityTooLarge
raise FallbackError, response.body
when Net::HTTPTooManyRequests
raise TooManyRequestsError, response.body
when Net::HTTPUnauthorized
raise BadAuthenticationError, uri.host if uri.userinfo
raise AuthenticationRequiredError, uri.host
when Net::HTTPForbidden
raise AuthenticationForbiddenError, uri.host
when Net::HTTPNotFound
raise FallbackError, "Net::HTTPNotFound: #{filtered_uri}"
else
raise HTTPError, "#{response.class}#{": #{response.body}" unless response.body.empty?}"
end
end
def request(uri, headers)
validate_uri_scheme!(uri)
filtered_uri = URICredentialsFilter.credential_filtered_uri(uri)
Bundler.ui.debug "HTTP GET #{filtered_uri}"
req = Net::HTTP::Get.new uri.request_uri, headers
if uri.user
user = CGI.unescape(uri.user)
password = uri.password ? CGI.unescape(uri.password) : nil
req.basic_auth(user, password)
end
connection.request(uri, req)
rescue OpenSSL::SSL::SSLError
raise CertificateFailureError.new(uri)
rescue *HTTP_ERRORS => e
Bundler.ui.trace e
if e.is_a?(SocketError) || e.message.to_s.include?("host down:")
raise NetworkDownError, "Could not reach host #{uri.host}. Check your network " \
"connection and try again."
else
raise HTTPError, "Network error while fetching #{filtered_uri}" \
" (#{e})"
end
end
private
def validate_uri_scheme!(uri)
return if /\Ahttps?\z/.match?(uri.scheme)
raise InvalidOption,
"The request uri `#{uri}` has an invalid scheme (`#{uri.scheme}`). " \
"Did you mean `http` or `https`?"
end
end
end
end

View File

@@ -1,25 +0,0 @@
# frozen_string_literal: true
require_relative "base"
module Bundler
class Fetcher
class Index < Base
def specs(_gem_names)
Bundler.rubygems.fetch_all_remote_specs(remote)
rescue Gem::RemoteFetcher::FetchError => e
case e.message
when /certificate verify failed/
raise CertificateFailureError.new(display_uri)
when /401/
raise BadAuthenticationError, remote_uri if remote_uri.userinfo
raise AuthenticationRequiredError, remote_uri
when /403/
raise AuthenticationForbiddenError, remote_uri
else
raise HTTPError, "Could not fetch specs from #{display_uri} due to underlying error <#{e.message}>"
end
end
end
end
end

View File

@@ -1,18 +0,0 @@
# frozen_string_literal: true
module Bundler
module ForcePlatform
private
# The `:force_ruby_platform` value used by dependencies for resolution, and
# by locked specifications for materialization is `false` by default, except
# for TruffleRuby. TruffleRuby generally needs to force the RUBY platform
# variant unless the name is explicitly allowlisted.
def default_force_ruby_platform
return false unless RUBY_ENGINE == "truffleruby"
!Gem::Platform::REUSE_AS_BINARY_ON_TRUFFLERUBY.include?(name)
end
end
end

View File

@@ -1,126 +0,0 @@
# frozen_string_literal: true
require_relative "vendored_thor"
module Bundler
module FriendlyErrors
module_function
def enable!
@disabled = false
end
def disabled?
@disabled
end
def disable!
@disabled = true
end
def log_error(error)
case error
when YamlSyntaxError
Bundler.ui.error error.message
Bundler.ui.trace error.orig_exception
when Dsl::DSLError, GemspecError
Bundler.ui.error error.message
when GemRequireError
Bundler.ui.error error.message
Bundler.ui.trace error.orig_exception
when BundlerError
if Bundler.ui.debug?
Bundler.ui.trace error
else
Bundler.ui.error error.message, :wrap => true
end
when Thor::Error
Bundler.ui.error error.message
when Interrupt
Bundler.ui.error "\nQuitting..."
Bundler.ui.trace error
when Gem::InvalidSpecificationException
Bundler.ui.error error.message, :wrap => true
when SystemExit
when *[defined?(Java::JavaLang::OutOfMemoryError) && Java::JavaLang::OutOfMemoryError].compact
Bundler.ui.error "\nYour JVM has run out of memory, and Bundler cannot continue. " \
"You can decrease the amount of memory Bundler needs by removing gems from your Gemfile, " \
"especially large gems. (Gems can be as large as hundreds of megabytes, and Bundler has to read those files!). " \
"Alternatively, you can increase the amount of memory the JVM is able to use by running Bundler with jruby -J-Xmx1024m -S bundle (JRuby defaults to 500MB)."
else request_issue_report_for(error)
end
end
def exit_status(error)
case error
when BundlerError then error.status_code
when Thor::Error then 15
when SystemExit then error.status
else 1
end
end
def request_issue_report_for(e)
Bundler.ui.error <<-EOS.gsub(/^ {8}/, ""), nil, nil
--- ERROR REPORT TEMPLATE -------------------------------------------------------
```
#{exception_message(e)}
```
#{Bundler::Env.report}
--- TEMPLATE END ----------------------------------------------------------------
EOS
Bundler.ui.error "Unfortunately, an unexpected error occurred, and Bundler cannot continue."
Bundler.ui.error <<-EOS.gsub(/^ {8}/, ""), nil, :yellow
First, try this link to see if there are any existing issue reports for this error:
#{issues_url(e)}
If there aren't any reports for this error yet, please fill in the new issue form located at #{new_issue_url}, and copy and paste the report template above in there.
EOS
end
def exception_message(error)
message = serialized_exception_for(error)
cause = error.cause
return message unless cause
message + serialized_exception_for(cause)
end
def serialized_exception_for(e)
<<-EOS.gsub(/^ {8}/, "")
#{e.class}: #{e.message}
#{e.backtrace&.join("\n ")&.chomp}
EOS
end
def issues_url(exception)
message = exception.message.lines.first.tr(":", " ").chomp
message = message.split("-").first if exception.is_a?(Errno)
require "cgi"
"https://github.com/rubygems/rubygems/search?q=" \
"#{CGI.escape(message)}&type=Issues"
end
def new_issue_url
"https://github.com/rubygems/rubygems/issues/new?labels=Bundler&template=bundler-related-issue.md"
end
end
def self.with_friendly_errors
FriendlyErrors.enable!
yield
rescue SignalException
raise
rescue Exception => e # rubocop:disable Lint/RescueException
raise if FriendlyErrors.disabled?
FriendlyErrors.log_error(e)
exit FriendlyErrors.exit_status(e)
end
end

View File

@@ -1,237 +0,0 @@
# frozen_string_literal: true
require_relative "../bundler"
require "shellwords"
module Bundler
class GemHelper
include Rake::DSL if defined? Rake::DSL
class << self
# set when install'd.
attr_accessor :instance
def install_tasks(opts = {})
new(opts[:dir], opts[:name]).install
end
def tag_prefix=(prefix)
instance.tag_prefix = prefix
end
def gemspec(&block)
gemspec = instance.gemspec
block&.call(gemspec)
gemspec
end
end
attr_reader :spec_path, :base, :gemspec
attr_writer :tag_prefix
def initialize(base = nil, name = nil)
@base = File.expand_path(base || SharedHelpers.pwd)
gemspecs = name ? [File.join(@base, "#{name}.gemspec")] : Gem::Util.glob_files_in_dir("{,*}.gemspec", @base)
raise "Unable to determine name from existing gemspec. Use :name => 'gemname' in #install_tasks to manually set it." unless gemspecs.size == 1
@spec_path = gemspecs.first
@gemspec = Bundler.load_gemspec(@spec_path)
@tag_prefix = ""
end
def install
built_gem_path = nil
desc "Build #{name}-#{version}.gem into the pkg directory."
task "build" do
built_gem_path = build_gem
end
desc "Generate SHA512 checksum if #{name}-#{version}.gem into the checksums directory."
task "build:checksum" => "build" do
build_checksum(built_gem_path)
end
desc "Build and install #{name}-#{version}.gem into system gems."
task "install" => "build" do
install_gem(built_gem_path)
end
desc "Build and install #{name}-#{version}.gem into system gems without network access."
task "install:local" => "build" do
install_gem(built_gem_path, :local)
end
desc "Create tag #{version_tag} and build and push #{name}-#{version}.gem to #{gem_push_host}\n" \
"To prevent publishing in RubyGems use `gem_push=no rake release`"
task "release", [:remote] => ["build", "release:guard_clean",
"release:source_control_push", "release:rubygem_push"] do
end
task "release:guard_clean" do
guard_clean
end
task "release:source_control_push", [:remote] do |_, args|
tag_version { git_push(args[:remote]) } unless already_tagged?
end
task "release:rubygem_push" => "build" do
rubygem_push(built_gem_path) if gem_push?
end
GemHelper.instance = self
end
def build_gem
file_name = nil
sh([*gem_command, "build", "-V", spec_path]) do
file_name = File.basename(built_gem_path)
SharedHelpers.filesystem_access(File.join(base, "pkg")) {|p| FileUtils.mkdir_p(p) }
FileUtils.mv(built_gem_path, "pkg")
Bundler.ui.confirm "#{name} #{version} built to pkg/#{file_name}."
end
File.join(base, "pkg", file_name)
end
def install_gem(built_gem_path = nil, local = false)
built_gem_path ||= build_gem
cmd = [*gem_command, "install", built_gem_path.to_s]
cmd << "--local" if local
sh(cmd)
Bundler.ui.confirm "#{name} (#{version}) installed."
end
def build_checksum(built_gem_path = nil)
built_gem_path ||= build_gem
SharedHelpers.filesystem_access(File.join(base, "checksums")) {|p| FileUtils.mkdir_p(p) }
file_name = "#{File.basename(built_gem_path)}.sha512"
require "digest/sha2"
checksum = ::Digest::SHA512.file(built_gem_path).hexdigest
target = File.join(base, "checksums", file_name)
File.write(target, checksum + "\n")
Bundler.ui.confirm "#{name} #{version} checksum written to checksums/#{file_name}."
end
protected
def rubygem_push(path)
cmd = [*gem_command, "push", path]
cmd << "--key" << gem_key if gem_key
cmd << "--host" << allowed_push_host if allowed_push_host
sh_with_input(cmd)
Bundler.ui.confirm "Pushed #{name} #{version} to #{gem_push_host}"
end
def built_gem_path
Gem::Util.glob_files_in_dir("#{name}-*.gem", base).sort_by {|f| File.mtime(f) }.last
end
def git_push(remote = nil)
remote ||= default_remote
sh("git push #{remote} refs/heads/#{current_branch}".shellsplit)
sh("git push #{remote} refs/tags/#{version_tag}".shellsplit)
Bundler.ui.confirm "Pushed git commits and release tag."
end
def default_remote
remote_for_branch, status = sh_with_status(%W[git config --get branch.#{current_branch}.remote])
return "origin" unless status.success?
remote_for_branch.strip
end
def current_branch
# We can replace this with `git branch --show-current` once we drop support for git < 2.22.0
sh(%w[git rev-parse --abbrev-ref HEAD]).gsub(%r{\Aheads/}, "").strip
end
def allowed_push_host
@gemspec.metadata["allowed_push_host"] if @gemspec.respond_to?(:metadata)
end
def gem_push_host
env_rubygems_host = ENV["RUBYGEMS_HOST"]
env_rubygems_host = nil if env_rubygems_host&.empty?
allowed_push_host || env_rubygems_host || "rubygems.org"
end
def already_tagged?
return false unless sh(%w[git tag]).split(/\n/).include?(version_tag)
Bundler.ui.confirm "Tag #{version_tag} has already been created."
true
end
def guard_clean
clean? && committed? || raise("There are files that need to be committed first.")
end
def clean?
sh_with_status(%w[git diff --exit-code])[1].success?
end
def committed?
sh_with_status(%w[git diff-index --quiet --cached HEAD])[1].success?
end
def tag_version
sh %W[git tag -m Version\ #{version} #{version_tag}]
Bundler.ui.confirm "Tagged #{version_tag}."
yield if block_given?
rescue RuntimeError
Bundler.ui.error "Untagging #{version_tag} due to error."
sh_with_status %W[git tag -d #{version_tag}]
raise
end
def version
gemspec.version
end
def version_tag
"#{@tag_prefix}v#{version}"
end
def name
gemspec.name
end
def sh_with_input(cmd)
Bundler.ui.debug(cmd)
SharedHelpers.chdir(base) do
abort unless Kernel.system(*cmd)
end
end
def sh(cmd, &block)
out, status = sh_with_status(cmd, &block)
unless status.success?
raise("Running `#{cmd.shelljoin}` failed with the following output:\n\n#{out}\n")
end
out
end
def sh_with_status(cmd, &block)
Bundler.ui.debug(cmd)
SharedHelpers.chdir(base) do
outbuf = IO.popen(cmd, :err => [:child, :out], &:read)
status = $?
block&.call(outbuf) if status.success?
[outbuf, status]
end
end
def gem_key
Bundler.settings["gem.push_key"].to_s.downcase if Bundler.settings["gem.push_key"]
end
def gem_push?
!%w[n no nil false off 0].include?(ENV["gem_push"].to_s.downcase)
end
def gem_command
ENV["GEM_COMMAND"]&.shellsplit || ["gem"]
end
end
end

View File

@@ -1,117 +0,0 @@
# frozen_string_literal: true
module Bundler
module GemHelpers
GENERIC_CACHE = { Gem::Platform::RUBY => Gem::Platform::RUBY } # rubocop:disable Style/MutableConstant
GENERICS = [
[Gem::Platform.new("java"), Gem::Platform.new("java")],
[Gem::Platform.new("mswin32"), Gem::Platform.new("mswin32")],
[Gem::Platform.new("mswin64"), Gem::Platform.new("mswin64")],
[Gem::Platform.new("universal-mingw32"), Gem::Platform.new("universal-mingw32")],
[Gem::Platform.new("x64-mingw32"), Gem::Platform.new("x64-mingw32")],
[Gem::Platform.new("x86_64-mingw32"), Gem::Platform.new("x64-mingw32")],
[Gem::Platform.new("x64-mingw-ucrt"), Gem::Platform.new("x64-mingw-ucrt")],
[Gem::Platform.new("mingw32"), Gem::Platform.new("x86-mingw32")],
].freeze
def generic(p)
GENERIC_CACHE[p] ||= begin
_, found = GENERICS.find do |match, _generic|
p.os == match.os && (!match.cpu || p.cpu == match.cpu)
end
found || Gem::Platform::RUBY
end
end
module_function :generic
def generic_local_platform
generic(local_platform)
end
module_function :generic_local_platform
def local_platform
Bundler.local_platform
end
module_function :local_platform
def platform_specificity_match(spec_platform, user_platform)
spec_platform = Gem::Platform.new(spec_platform)
PlatformMatch.specificity_score(spec_platform, user_platform)
end
module_function :platform_specificity_match
def select_best_platform_match(specs, platform)
matching = specs.select {|spec| spec.match_platform(platform) }
sort_best_platform_match(matching, platform)
end
module_function :select_best_platform_match
def sort_best_platform_match(matching, platform)
exact = matching.select {|spec| spec.platform == platform }
return exact if exact.any?
sorted_matching = matching.sort_by {|spec| platform_specificity_match(spec.platform, platform) }
exemplary_spec = sorted_matching.first
sorted_matching.take_while {|spec| same_specificity(platform, spec, exemplary_spec) && same_deps(spec, exemplary_spec) }
end
module_function :sort_best_platform_match
class PlatformMatch
def self.specificity_score(spec_platform, user_platform)
return -1 if spec_platform == user_platform
return 1_000_000 if spec_platform.nil? || spec_platform == Gem::Platform::RUBY || user_platform == Gem::Platform::RUBY
os_match(spec_platform, user_platform) +
cpu_match(spec_platform, user_platform) * 10 +
platform_version_match(spec_platform, user_platform) * 100
end
def self.os_match(spec_platform, user_platform)
if spec_platform.os == user_platform.os
0
else
1
end
end
def self.cpu_match(spec_platform, user_platform)
if spec_platform.cpu == user_platform.cpu
0
elsif spec_platform.cpu == "arm" && user_platform.cpu.to_s.start_with?("arm")
0
elsif spec_platform.cpu.nil? || spec_platform.cpu == "universal"
1
else
2
end
end
def self.platform_version_match(spec_platform, user_platform)
if spec_platform.version == user_platform.version
0
elsif spec_platform.version.nil?
1
else
2
end
end
end
def same_specificity(platform, spec, exemplary_spec)
platform_specificity_match(spec.platform, platform) == platform_specificity_match(exemplary_spec.platform, platform)
end
module_function :same_specificity
def same_deps(spec, exemplary_spec)
same_runtime_deps = spec.dependencies.sort == exemplary_spec.dependencies.sort
return same_runtime_deps unless spec.is_a?(Gem::Specification) && exemplary_spec.is_a?(Gem::Specification)
same_metadata_deps = spec.required_ruby_version == exemplary_spec.required_ruby_version && spec.required_rubygems_version == exemplary_spec.required_rubygems_version
same_runtime_deps && same_metadata_deps
end
module_function :same_deps
end
end

View File

@@ -1,7 +0,0 @@
# frozen_string_literal: true
require "rake/clean"
CLOBBER.include "pkg"
require_relative "gem_helper"
Bundler::GemHelper.install_tasks

View File

@@ -1,145 +0,0 @@
# frozen_string_literal: true
module Bundler
# This class contains all of the logic for determining the next version of a
# Gem to update to based on the requested level (patch, minor, major).
# Primarily designed to work with Resolver which will provide it the list of
# available dependency versions as found in its index, before returning it to
# to the resolution engine to select the best version.
class GemVersionPromoter
attr_reader :level
attr_accessor :pre
# By default, strict is false, meaning every available version of a gem
# is returned from sort_versions. The order gives preference to the
# requested level (:patch, :minor, :major) but in complicated requirement
# cases some gems will by necessity be promoted past the requested level,
# or even reverted to older versions.
#
# If strict is set to true, the results from sort_versions will be
# truncated, eliminating any version outside the current level scope.
# This can lead to unexpected outcomes or even VersionConflict exceptions
# that report a version of a gem not existing for versions that indeed do
# existing in the referenced source.
attr_accessor :strict
# Creates a GemVersionPromoter instance.
#
# @return [GemVersionPromoter]
def initialize
@level = :major
@strict = false
@pre = false
end
# @param value [Symbol] One of three Symbols: :major, :minor or :patch.
def level=(value)
v = case value
when String, Symbol
value.to_sym
end
raise ArgumentError, "Unexpected level #{v}. Must be :major, :minor or :patch" unless [:major, :minor, :patch].include?(v)
@level = v
end
# Given a Resolver::Package and an Array of Specifications of available
# versions for a gem, this method will return the Array of Specifications
# sorted (and possibly truncated if strict is true) in an order to give
# preference to the current level (:major, :minor or :patch) when resolution
# is deciding what versions best resolve all dependencies in the bundle.
# @param package [Resolver::Package] The package being resolved.
# @param specs [Specification] An array of Specifications for the package.
# @return [Specification] A new instance of the Specification Array sorted and
# possibly filtered.
def sort_versions(package, specs)
specs = filter_dep_specs(specs, package) if strict
sort_dep_specs(specs, package)
end
# @return [bool] Convenience method for testing value of level variable.
def major?
level == :major
end
# @return [bool] Convenience method for testing value of level variable.
def minor?
level == :minor
end
# @return [bool] Convenience method for testing value of pre variable.
def pre?
pre == true
end
private
def filter_dep_specs(specs, package)
locked_version = package.locked_version
return specs if locked_version.nil? || major?
specs.select do |spec|
gsv = spec.version
must_match = minor? ? [0] : [0, 1]
all_match = must_match.all? {|idx| gsv.segments[idx] == locked_version.segments[idx] }
all_match && gsv >= locked_version
end
end
def sort_dep_specs(specs, package)
locked_version = package.locked_version
result = specs.sort do |a, b|
unless package.prerelease_specified? || pre?
a_pre = a.prerelease?
b_pre = b.prerelease?
next -1 if a_pre && !b_pre
next 1 if b_pre && !a_pre
end
if major?
a <=> b
elsif either_version_older_than_locked?(a, b, locked_version)
a <=> b
elsif segments_do_not_match?(a, b, :major)
b <=> a
elsif !minor? && segments_do_not_match?(a, b, :minor)
b <=> a
else
a <=> b
end
end
post_sort(result, package.unlock?, locked_version)
end
def either_version_older_than_locked?(a, b, locked_version)
locked_version && (a.version < locked_version || b.version < locked_version)
end
def segments_do_not_match?(a, b, level)
index = [:major, :minor].index(level)
a.segments[index] != b.segments[index]
end
# Specific version moves can't always reliably be done during sorting
# as not all elements are compared against each other.
def post_sort(result, unlock, locked_version)
# default :major behavior in Bundler does not do this
return result if major?
if unlock || locked_version.nil?
result
else
move_version_to_end(result, locked_version)
end
end
def move_version_to_end(result, version)
move, keep = result.partition {|s| s.version.to_s == version.to_s }
keep.concat(move)
end
end
end

View File

@@ -1,152 +0,0 @@
# frozen_string_literal: true
require "set"
module Bundler
class Graph
GRAPH_NAME = :Gemfile
def initialize(env, output_file, show_version = false, show_requirements = false, output_format = "png", without = [])
@env = env
@output_file = output_file
@show_version = show_version
@show_requirements = show_requirements
@output_format = output_format
@without_groups = without.map(&:to_sym)
@groups = []
@relations = Hash.new {|h, k| h[k] = Set.new }
@node_options = {}
@edge_options = {}
_populate_relations
end
attr_reader :groups, :relations, :node_options, :edge_options, :output_file, :output_format
def viz
GraphVizClient.new(self).run
end
private
def _populate_relations
parent_dependencies = _groups.values.to_set.flatten
loop do
break if parent_dependencies.empty?
tmp = Set.new
parent_dependencies.each do |dependency|
child_dependencies = spec_for_dependency(dependency).runtime_dependencies.to_set
@relations[dependency.name] += child_dependencies.map(&:name).to_set
tmp += child_dependencies
@node_options[dependency.name] = _make_label(dependency, :node)
child_dependencies.each do |c_dependency|
@edge_options["#{dependency.name}_#{c_dependency.name}"] = _make_label(c_dependency, :edge)
end
end
parent_dependencies = tmp
end
end
def _groups
relations = Hash.new {|h, k| h[k] = Set.new }
@env.current_dependencies.each do |dependency|
dependency.groups.each do |group|
next if @without_groups.include?(group)
relations[group.to_s].add(dependency)
@relations[group.to_s].add(dependency.name)
@node_options[group.to_s] ||= _make_label(group, :node)
@edge_options["#{group}_#{dependency.name}"] = _make_label(dependency, :edge)
end
end
@groups = relations.keys
relations
end
def _make_label(symbol_or_string_or_dependency, element_type)
case element_type.to_sym
when :node
if symbol_or_string_or_dependency.is_a?(Gem::Dependency)
label = symbol_or_string_or_dependency.name.dup
label << "\n#{spec_for_dependency(symbol_or_string_or_dependency).version}" if @show_version
else
label = symbol_or_string_or_dependency.to_s
end
when :edge
label = nil
if symbol_or_string_or_dependency.respond_to?(:requirements_list) && @show_requirements
tmp = symbol_or_string_or_dependency.requirements_list.join(", ")
label = tmp if tmp != ">= 0"
end
else
raise ArgumentError, "2nd argument is invalid"
end
label.nil? ? {} : { :label => label }
end
def spec_for_dependency(dependency)
@env.requested_specs.find {|s| s.name == dependency.name }
end
class GraphVizClient
def initialize(graph_instance)
@graph_name = graph_instance.class::GRAPH_NAME
@groups = graph_instance.groups
@relations = graph_instance.relations
@node_options = graph_instance.node_options
@edge_options = graph_instance.edge_options
@output_file = graph_instance.output_file
@output_format = graph_instance.output_format
end
def g
@g ||= ::GraphViz.digraph(@graph_name, :concentrate => true, :normalize => true, :nodesep => 0.55) do |g|
g.edge[:weight] = 2
g.edge[:fontname] = g.node[:fontname] = "Arial, Helvetica, SansSerif"
g.edge[:fontsize] = 12
end
end
def run
@groups.each do |group|
g.add_nodes(
group, {
:style => "filled",
:fillcolor => "#B9B9D5",
:shape => "box3d",
:fontsize => 16,
}.merge(@node_options[group])
)
end
@relations.each do |parent, children|
children.each do |child|
if @groups.include?(parent)
g.add_nodes(child, { :style => "filled", :fillcolor => "#B9B9D5" }.merge(@node_options[child]))
g.add_edges(parent, child, { :constraint => false }.merge(@edge_options["#{parent}_#{child}"]))
else
g.add_nodes(child, @node_options[child])
g.add_edges(parent, child, @edge_options["#{parent}_#{child}"])
end
end
end
if @output_format.to_s == "debug"
$stdout.puts g.output :none => String
Bundler.ui.info "debugging bundle viz..."
else
begin
g.output @output_format.to_sym => "#{@output_file}.#{@output_format}"
Bundler.ui.info "#{@output_file}.#{@output_format}"
rescue ArgumentError => e
warn "Unsupported output format. See Ruby-Graphviz/lib/graphviz/constants.rb"
raise e
end
end
end
end
end
end

View File

@@ -1,175 +0,0 @@
# frozen_string_literal: true
module Bundler
class Index
include Enumerable
def self.build
i = new
yield i
i
end
attr_reader :specs, :all_specs, :sources
protected :specs, :all_specs
RUBY = "ruby"
NULL = "\0"
def initialize
@sources = []
@cache = {}
@specs = Hash.new {|h, k| h[k] = {} }
@all_specs = Hash.new {|h, k| h[k] = EMPTY_SEARCH }
end
def initialize_copy(o)
@sources = o.sources.dup
@cache = {}
@specs = Hash.new {|h, k| h[k] = {} }
@all_specs = Hash.new {|h, k| h[k] = EMPTY_SEARCH }
o.specs.each do |name, hash|
@specs[name] = hash.dup
end
o.all_specs.each do |name, array|
@all_specs[name] = array.dup
end
end
def inspect
"#<#{self.class}:0x#{object_id} sources=#{sources.map(&:inspect)} specs.size=#{specs.size}>"
end
def empty?
each { return false }
true
end
def search_all(name)
all_matches = local_search(name) + @all_specs[name]
@sources.each do |source|
all_matches.concat(source.search_all(name))
end
all_matches
end
# Search this index's specs, and any source indexes that this index knows
# about, returning all of the results.
def search(query)
results = local_search(query)
return results unless @sources.any?
@sources.each do |source|
results.concat(source.search(query))
end
results.uniq(&:full_name)
end
def local_search(query)
case query
when Gem::Specification, RemoteSpecification, LazySpecification, EndpointSpecification then search_by_spec(query)
when String then specs_by_name(query)
when Array then specs_by_name_and_version(*query)
else
raise "You can't search for a #{query.inspect}."
end
end
alias_method :[], :search
def <<(spec)
@specs[spec.name][spec.full_name] = spec
spec
end
def each(&blk)
return enum_for(:each) unless blk
specs.values.each do |spec_sets|
spec_sets.values.each(&blk)
end
sources.each {|s| s.each(&blk) }
self
end
def spec_names
names = specs.keys + sources.map(&:spec_names)
names.uniq!
names
end
def unmet_dependency_names
dependency_names.select do |name|
search(name).empty?
end
end
def dependency_names
names = []
each do |spec|
spec.dependencies.each do |dep|
next if dep.type == :development
names << dep.name
end
end
names.uniq
end
def use(other, override_dupes = false)
return unless other
other.each do |s|
if (dupes = search_by_spec(s)) && !dupes.empty?
# safe to << since it's a new array when it has contents
@all_specs[s.name] = dupes << s
next unless override_dupes
end
self << s
end
self
end
def size
@sources.inject(@specs.size) do |size, source|
size += source.size
end
end
# Whether all the specs in self are in other
# TODO: rename to #include?
def ==(other)
all? do |spec|
other_spec = other[spec].first
other_spec && dependencies_eql?(spec, other_spec) && spec.source == other_spec.source
end
end
def dependencies_eql?(spec, other_spec)
deps = spec.dependencies.select {|d| d.type != :development }
other_deps = other_spec.dependencies.select {|d| d.type != :development }
deps.sort == other_deps.sort
end
def add_source(index)
raise ArgumentError, "Source must be an index, not #{index.class}" unless index.is_a?(Index)
@sources << index
@sources.uniq! # need to use uniq! here instead of checking for the item before adding
end
private
def specs_by_name_and_version(name, version)
specs_by_name(name).select {|spec| spec.version == version }
end
def specs_by_name(name)
@specs[name].values
end
EMPTY_SEARCH = [].freeze
def search_by_spec(spec)
spec = @specs[spec.name][spec.full_name]
spec ? [spec] : EMPTY_SEARCH
end
end
end

View File

@@ -1,287 +0,0 @@
# frozen_string_literal: true
module Bundler
class Injector
INJECTED_GEMS = "injected gems"
def self.inject(new_deps, options = {})
injector = new(new_deps, options)
injector.inject(Bundler.default_gemfile, Bundler.default_lockfile)
end
def self.remove(gems, options = {})
injector = new(gems, options)
injector.remove(Bundler.default_gemfile, Bundler.default_lockfile)
end
def initialize(deps, options = {})
@deps = deps
@options = options
end
# @param [Pathname] gemfile_path The Gemfile in which to inject the new dependency.
# @param [Pathname] lockfile_path The lockfile in which to inject the new dependency.
# @return [Array]
def inject(gemfile_path, lockfile_path)
if Bundler.frozen_bundle?
# ensure the lock and Gemfile are synced
Bundler.definition.ensure_equivalent_gemfile_and_lockfile(true)
end
# temporarily unfreeze
Bundler.settings.temporary(:deployment => false, :frozen => false) do
# evaluate the Gemfile we have now
builder = Dsl.new
builder.eval_gemfile(gemfile_path)
# don't inject any gems that are already in the Gemfile
@deps -= builder.dependencies
# add new deps to the end of the in-memory Gemfile
# Set conservative versioning to false because
# we want to let the resolver resolve the version first
builder.eval_gemfile(INJECTED_GEMS, build_gem_lines(false)) if @deps.any?
# resolve to see if the new deps broke anything
@definition = builder.to_definition(lockfile_path, {})
@definition.resolve_remotely!
# since nothing broke, we can add those gems to the gemfile
append_to(gemfile_path, build_gem_lines(@options[:conservative_versioning])) if @deps.any?
# since we resolved successfully, write out the lockfile
@definition.lock(Bundler.default_lockfile)
# invalidate the cached Bundler.definition
Bundler.reset_paths!
# return an array of the deps that we added
@deps
end
end
# @param [Pathname] gemfile_path The Gemfile from which to remove dependencies.
# @param [Pathname] lockfile_path The lockfile from which to remove dependencies.
# @return [Array]
def remove(gemfile_path, lockfile_path)
# remove gems from each gemfiles we have
Bundler.definition.gemfiles.each do |path|
deps = remove_deps(path)
show_warning("No gems were removed from the gemfile.") if deps.empty?
deps.each {|dep| Bundler.ui.confirm "#{SharedHelpers.pretty_dependency(dep)} was removed." }
end
# Invalidate the cached Bundler.definition.
# This prevents e.g. `bundle remove ...` from using outdated information.
Bundler.reset_paths!
end
private
def conservative_version(spec)
version = spec.version
return ">= 0" if version.nil?
segments = version.segments
seg_end_index = version >= Gem::Version.new("1.0") ? 1 : 2
prerelease_suffix = version.to_s.gsub(version.release.to_s, "") if version.prerelease?
"#{version_prefix}#{segments[0..seg_end_index].join(".")}#{prerelease_suffix}"
end
def version_prefix
if @options[:strict]
"= "
elsif @options[:optimistic]
">= "
else
"~> "
end
end
def build_gem_lines(conservative_versioning)
@deps.map do |d|
name = d.name.dump
requirement = if conservative_versioning
", \"#{conservative_version(@definition.specs[d.name][0])}\""
else
", #{d.requirement.as_list.map(&:dump).join(", ")}"
end
if d.groups != Array(:default)
group = d.groups.size == 1 ? ", :group => #{d.groups.first.inspect}" : ", :groups => #{d.groups.inspect}"
end
source = ", :source => \"#{d.source}\"" unless d.source.nil?
path = ", :path => \"#{d.path}\"" unless d.path.nil?
git = ", :git => \"#{d.git}\"" unless d.git.nil?
github = ", :github => \"#{d.github}\"" unless d.github.nil?
branch = ", :branch => \"#{d.branch}\"" unless d.branch.nil?
ref = ", :ref => \"#{d.ref}\"" unless d.ref.nil?
require_path = ", :require => #{convert_autorequire(d.autorequire)}" unless d.autorequire.nil?
%(gem #{name}#{requirement}#{group}#{source}#{path}#{git}#{github}#{branch}#{ref}#{require_path})
end.join("\n")
end
def append_to(gemfile_path, new_gem_lines)
gemfile_path.open("a") do |f|
f.puts
f.puts new_gem_lines
end
end
# evaluates a gemfile to remove the specified gem
# from it.
def remove_deps(gemfile_path)
initial_gemfile = File.readlines(gemfile_path)
Bundler.ui.info "Removing gems from #{gemfile_path}"
# evaluate the Gemfile we have
builder = Dsl.new
builder.eval_gemfile(gemfile_path)
removed_deps = remove_gems_from_dependencies(builder, @deps, gemfile_path)
# abort the operation if no gems were removed
# no need to operate on gemfile further
return [] if removed_deps.empty?
cleaned_gemfile = remove_gems_from_gemfile(@deps, gemfile_path)
SharedHelpers.write_to_gemfile(gemfile_path, cleaned_gemfile)
# check for errors
# including extra gems being removed
# or some gems not being removed
# and return the actual removed deps
cross_check_for_errors(gemfile_path, builder.dependencies, removed_deps, initial_gemfile)
end
# @param [Dsl] builder Dsl object of current Gemfile.
# @param [Array] gems Array of names of gems to be removed.
# @param [Pathname] gemfile_path Path of the Gemfile.
# @return [Array] Array of removed dependencies.
def remove_gems_from_dependencies(builder, gems, gemfile_path)
removed_deps = []
gems.each do |gem_name|
deleted_dep = builder.dependencies.find {|d| d.name == gem_name }
if deleted_dep.nil?
raise GemfileError, "`#{gem_name}` is not specified in #{gemfile_path} so it could not be removed."
end
builder.dependencies.delete(deleted_dep)
removed_deps << deleted_dep
end
removed_deps
end
# @param [Array] gems Array of names of gems to be removed.
# @param [Pathname] gemfile_path The Gemfile from which to remove dependencies.
def remove_gems_from_gemfile(gems, gemfile_path)
patterns = /gem\s+(['"])#{Regexp.union(gems)}\1|gem\s*\((['"])#{Regexp.union(gems)}\2\)/
new_gemfile = []
multiline_removal = false
File.readlines(gemfile_path).each do |line|
match_data = line.match(patterns)
if match_data && is_not_within_comment?(line, match_data)
multiline_removal = line.rstrip.end_with?(",")
# skip lines which match the regex
next
end
# skip followup lines until line does not end with ','
new_gemfile << line unless multiline_removal
multiline_removal = line.rstrip.end_with?(",") if multiline_removal
end
# remove line \n and append them with other strings
new_gemfile.each_with_index do |_line, index|
if new_gemfile[index + 1] == "\n"
new_gemfile[index] += new_gemfile[index + 1]
new_gemfile.delete_at(index + 1)
end
end
%w[group source env install_if].each {|block| remove_nested_blocks(new_gemfile, block) }
new_gemfile.join.chomp
end
# @param [String] line Individual line of gemfile content.
# @param [MatchData] match_data Data about Regex match.
def is_not_within_comment?(line, match_data)
match_start_index = match_data.offset(0).first
!line[0..match_start_index].include?("#")
end
# @param [Array] gemfile Array of gemfile contents.
# @param [String] block_name Name of block name to look for.
def remove_nested_blocks(gemfile, block_name)
nested_blocks = 0
# count number of nested blocks
gemfile.each_with_index {|line, index| nested_blocks += 1 if !gemfile[index + 1].nil? && gemfile[index + 1].include?(block_name) && line.include?(block_name) }
while nested_blocks >= 0
nested_blocks -= 1
gemfile.each_with_index do |line, index|
next unless !line.nil? && line.strip.start_with?(block_name)
if /^\s*end\s*$/.match?(gemfile[index + 1])
gemfile[index] = nil
gemfile[index + 1] = nil
end
end
gemfile.compact!
end
end
# @param [Pathname] gemfile_path The Gemfile from which to remove dependencies.
# @param [Array] original_deps Array of original dependencies.
# @param [Array] removed_deps Array of removed dependencies.
# @param [Array] initial_gemfile Contents of original Gemfile before any operation.
def cross_check_for_errors(gemfile_path, original_deps, removed_deps, initial_gemfile)
# evaluate the new gemfile to look for any failure cases
builder = Dsl.new
builder.eval_gemfile(gemfile_path)
# record gems which were removed but not requested
extra_removed_gems = original_deps - builder.dependencies
# if some extra gems were removed then raise error
# and revert Gemfile to original
unless extra_removed_gems.empty?
SharedHelpers.write_to_gemfile(gemfile_path, initial_gemfile.join)
raise InvalidOption, "Gems could not be removed. #{extra_removed_gems.join(", ")} would also have been removed. Bundler cannot continue."
end
# record gems which could not be removed due to some reasons
errored_deps = builder.dependencies.select {|d| d.gemfile == gemfile_path } & removed_deps.select {|d| d.gemfile == gemfile_path }
show_warning "#{errored_deps.map(&:name).join(", ")} could not be removed." unless errored_deps.empty?
# return actual removed dependencies
removed_deps - errored_deps
end
def show_warning(message)
Bundler.ui.info Bundler.ui.add_color(message, :yellow)
end
def convert_autorequire(autorequire)
autorequire = autorequire.first
return autorequire if autorequire == "false"
autorequire.inspect
end
end
end

View File

@@ -1,73 +0,0 @@
# frozen_string_literal: true
# Allows for declaring a Gemfile inline in a ruby script, optionally installing
# any gems that aren't already installed on the user's system.
#
# @note Every gem that is specified in this 'Gemfile' will be `require`d, as if
# the user had manually called `Bundler.require`. To avoid a requested gem
# being automatically required, add the `:require => false` option to the
# `gem` dependency declaration.
#
# @param install [Boolean] whether gems that aren't already installed on the
# user's system should be installed.
# Defaults to `false`.
#
# @param gemfile [Proc] a block that is evaluated as a `Gemfile`.
#
# @example Using an inline Gemfile
#
# #!/usr/bin/env ruby
#
# require 'bundler/inline'
#
# gemfile do
# source 'https://rubygems.org'
# gem 'json', require: false
# gem 'nap', require: 'rest'
# gem 'cocoapods', '~> 0.34.1'
# end
#
# puts Pod::VERSION # => "0.34.4"
#
def gemfile(install = false, options = {}, &gemfile)
require_relative "../bundler"
Bundler.reset!
opts = options.dup
ui = opts.delete(:ui) { Bundler::UI::Shell.new }
ui.level = "silent" if opts.delete(:quiet) || !install
Bundler.ui = ui
raise ArgumentError, "Unknown options: #{opts.keys.join(", ")}" unless opts.empty?
Bundler.with_unbundled_env do
Bundler.instance_variable_set(:@bundle_path, Pathname.new(Gem.dir))
Bundler::SharedHelpers.set_env "BUNDLE_GEMFILE", "Gemfile"
Bundler::Plugin.gemfile_install(&gemfile) if Bundler.feature_flag.plugins?
builder = Bundler::Dsl.new
builder.instance_eval(&gemfile)
builder.check_primary_source_safety
Bundler.settings.temporary(:deployment => false, :frozen => false) do
definition = builder.to_definition(nil, true)
def definition.lock(*); end
definition.validate_runtime!
if install || definition.missing_specs?
Bundler.settings.temporary(:inline => true, :no_install => false) do
installer = Bundler::Installer.install(Bundler.root, definition, :system => true)
installer.post_install_messages.each do |name, message|
Bundler.ui.info "Post-install message from #{name}:\n#{message}"
end
end
end
runtime = Bundler::Runtime.new(nil, definition)
runtime.setup.require
end
end
if ENV["BUNDLE_GEMFILE"].nil?
ENV["BUNDLE_GEMFILE"] = ""
end
end

View File

@@ -1,267 +0,0 @@
# frozen_string_literal: true
require_relative "worker"
require_relative "installer/parallel_installer"
require_relative "installer/standalone"
require_relative "installer/gem_installer"
module Bundler
class Installer
class << self
attr_accessor :ambiguous_gems
Installer.ambiguous_gems = []
end
attr_reader :post_install_messages, :definition
# Begins the installation process for Bundler.
# For more information see the #run method on this class.
def self.install(root, definition, options = {})
installer = new(root, definition)
Plugin.hook(Plugin::Events::GEM_BEFORE_INSTALL_ALL, definition.dependencies)
installer.run(options)
Plugin.hook(Plugin::Events::GEM_AFTER_INSTALL_ALL, definition.dependencies)
installer
end
def initialize(root, definition)
@root = root
@definition = definition
@post_install_messages = {}
end
# Runs the install procedures for a specific Gemfile.
#
# Firstly, this method will check to see if `Bundler.bundle_path` exists
# and if not then Bundler will create the directory. This is usually the same
# location as RubyGems which typically is the `~/.gem` directory
# unless other specified.
#
# Secondly, it checks if Bundler has been configured to be "frozen".
# Frozen ensures that the Gemfile and the Gemfile.lock file are matching.
# This stops a situation where a developer may update the Gemfile but may not run
# `bundle install`, which leads to the Gemfile.lock file not being correctly updated.
# If this file is not correctly updated then any other developer running
# `bundle install` will potentially not install the correct gems.
#
# Thirdly, Bundler checks if there are any dependencies specified in the Gemfile.
# If there are no dependencies specified then Bundler returns a warning message stating
# so and this method returns.
#
# Fourthly, Bundler checks if the Gemfile.lock exists, and if so
# then proceeds to set up a definition based on the Gemfile and the Gemfile.lock.
# During this step Bundler will also download information about any new gems
# that are not in the Gemfile.lock and resolve any dependencies if needed.
#
# Fifthly, Bundler resolves the dependencies either through a cache of gems or by remote.
# This then leads into the gems being installed, along with stubs for their executables,
# but only if the --binstubs option has been passed or Bundler.options[:bin] has been set
# earlier.
#
# Sixthly, a new Gemfile.lock is created from the installed gems to ensure that the next time
# that a user runs `bundle install` they will receive any updates from this process.
#
# Finally, if the user has specified the standalone flag, Bundler will generate the needed
# require paths and save them in a `setup.rb` file. See `bundle standalone --help` for more
# information.
def run(options)
Bundler.create_bundle_path
ProcessLock.lock do
if Bundler.frozen_bundle?
@definition.ensure_equivalent_gemfile_and_lockfile(options[:deployment])
end
if @definition.dependencies.empty?
Bundler.ui.warn "The Gemfile specifies no dependencies"
lock
return
end
if resolve_if_needed(options)
ensure_specs_are_compatible!
load_plugins
options.delete(:jobs)
else
options[:jobs] = 1 # to avoid the overhead of Bundler::Worker
end
install(options)
Gem::Specification.reset # invalidate gem specification cache so that installed gems are immediately available
lock
Standalone.new(options[:standalone], @definition).generate if options[:standalone]
end
end
def generate_bundler_executable_stubs(spec, options = {})
if options[:binstubs_cmd] && spec.executables.empty?
options = {}
spec.runtime_dependencies.each do |dep|
bins = @definition.specs[dep].first.executables
options[dep.name] = bins unless bins.empty?
end
if options.any?
Bundler.ui.warn "#{spec.name} has no executables, but you may want " \
"one from a gem it depends on."
options.each {|name, bins| Bundler.ui.warn " #{name} has: #{bins.join(", ")}" }
else
Bundler.ui.warn "There are no executables for the gem #{spec.name}."
end
return
end
# double-assignment to avoid warnings about variables that will be used by ERB
bin_path = Bundler.bin_path
bin_path = bin_path
relative_gemfile_path = Bundler.default_gemfile.relative_path_from(bin_path)
relative_gemfile_path = relative_gemfile_path
ruby_command = Thor::Util.ruby_command
ruby_command = ruby_command
template_path = File.expand_path("templates/Executable", __dir__)
if spec.name == "bundler"
template_path += ".bundler"
spec.executables = %(bundle)
end
template = File.read(template_path)
exists = []
spec.executables.each do |executable|
binstub_path = "#{bin_path}/#{executable}"
if File.exist?(binstub_path) && !options[:force]
exists << executable
next
end
mode = Gem.win_platform? ? "wb:UTF-8" : "w"
require "erb"
content = ERB.new(template, :trim_mode => "-").result(binding)
File.write(binstub_path, content, :mode => mode, :perm => 0o777 & ~File.umask)
if Gem.win_platform? || options[:all_platforms]
prefix = "@ruby -x \"%~f0\" %*\n@exit /b %ERRORLEVEL%\n\n"
File.write("#{binstub_path}.cmd", prefix + content, :mode => mode)
end
end
if options[:binstubs_cmd] && exists.any?
case exists.size
when 1
Bundler.ui.warn "Skipped #{exists[0]} since it already exists."
when 2
Bundler.ui.warn "Skipped #{exists.join(" and ")} since they already exist."
else
items = exists[0...-1].empty? ? nil : exists[0...-1].join(", ")
skipped = [items, exists[-1]].compact.join(" and ")
Bundler.ui.warn "Skipped #{skipped} since they already exist."
end
Bundler.ui.warn "If you want to overwrite skipped stubs, use --force."
end
end
def generate_standalone_bundler_executable_stubs(spec, options = {})
# double-assignment to avoid warnings about variables that will be used by ERB
bin_path = Bundler.bin_path
unless path = Bundler.settings[:path]
raise "Can't standalone without an explicit path set"
end
standalone_path = Bundler.root.join(path).relative_path_from(bin_path)
standalone_path = standalone_path
template = File.read(File.expand_path("templates/Executable.standalone", __dir__))
ruby_command = Thor::Util.ruby_command
ruby_command = ruby_command
spec.executables.each do |executable|
next if executable == "bundle"
executable_path = Pathname(spec.full_gem_path).join(spec.bindir, executable).relative_path_from(bin_path)
executable_path = executable_path
mode = Gem.win_platform? ? "wb:UTF-8" : "w"
require "erb"
content = ERB.new(template, :trim_mode => "-").result(binding)
File.write("#{bin_path}/#{executable}", content, :mode => mode, :perm => 0o755)
if Gem.win_platform? || options[:all_platforms]
prefix = "@ruby -x \"%~f0\" %*\n@exit /b %ERRORLEVEL%\n\n"
File.write("#{bin_path}/#{executable}.cmd", prefix + content, :mode => mode)
end
end
end
private
# the order that the resolver provides is significant, since
# dependencies might affect the installation of a gem.
# that said, it's a rare situation (other than rake), and parallel
# installation is SO MUCH FASTER. so we let people opt in.
def install(options)
force = options["force"]
jobs = installation_parallelization(options)
install_in_parallel jobs, options[:standalone], force
end
def installation_parallelization(options)
if jobs = options.delete(:jobs)
return jobs
end
if jobs = Bundler.settings[:jobs]
return jobs
end
Bundler.settings.processor_count
end
def load_plugins
Bundler.rubygems.load_plugins
requested_path_gems = @definition.requested_specs.select {|s| s.source.is_a?(Source::Path) }
path_plugin_files = requested_path_gems.map do |spec|
Bundler.rubygems.spec_matches_for_glob(spec, "rubygems_plugin#{Bundler.rubygems.suffix_pattern}")
rescue TypeError
error_message = "#{spec.name} #{spec.version} has an invalid gemspec"
raise Gem::InvalidSpecificationException, error_message
end.flatten
Bundler.rubygems.load_plugin_files(path_plugin_files)
Bundler.rubygems.load_env_plugins
end
def ensure_specs_are_compatible!
@definition.specs.each do |spec|
unless spec.matches_current_ruby?
raise InstallError, "#{spec.full_name} requires ruby version #{spec.required_ruby_version}, " \
"which is incompatible with the current version, #{Gem.ruby_version}"
end
unless spec.matches_current_rubygems?
raise InstallError, "#{spec.full_name} requires rubygems version #{spec.required_rubygems_version}, " \
"which is incompatible with the current version, #{Gem.rubygems_version}"
end
end
end
def install_in_parallel(size, standalone, force = false)
spec_installations = ParallelInstaller.call(self, @definition.specs, size, standalone, force)
spec_installations.each do |installation|
post_install_messages[installation.name] = installation.post_install_message if installation.has_post_install_message?
end
end
# returns whether or not a re-resolve was needed
def resolve_if_needed(options)
@definition.resolution_mode = options
if !@definition.unlocking? && !options["force"] && !Bundler.settings[:inline] && Bundler.default_lockfile.file?
return false if @definition.nothing_changed? && !@definition.missing_specs?
end
@definition.setup_sources_for_resolve
true
end
def lock(opts = {})
@definition.lock(Bundler.default_lockfile, opts[:preserve_unknown_sections])
end
end
end

View File

@@ -1,84 +0,0 @@
# frozen_string_literal: true
module Bundler
class GemInstaller
attr_reader :spec, :standalone, :worker, :force, :installer
def initialize(spec, installer, standalone = false, worker = 0, force = false)
@spec = spec
@installer = installer
@standalone = standalone
@worker = worker
@force = force
end
def install_from_spec
post_install_message = install
Bundler.ui.debug "#{worker}: #{spec.name} (#{spec.version}) from #{spec.loaded_from}"
generate_executable_stubs
return true, post_install_message
rescue Bundler::InstallHookError, Bundler::SecurityError, Bundler::APIResponseMismatchError
raise
rescue Errno::ENOSPC
return false, out_of_space_message
rescue Bundler::BundlerError, Gem::InstallError, Bundler::APIResponseInvalidDependenciesError => e
return false, specific_failure_message(e)
end
private
def specific_failure_message(e)
message = "#{e.class}: #{e.message}\n"
message += " " + e.backtrace.join("\n ") + "\n\n"
message = message.lines.first + Bundler.ui.add_color(message.lines.drop(1).join, :clear)
message + Bundler.ui.add_color(failure_message, :red)
end
def failure_message
install_error_message
end
def install_error_message
"An error occurred while installing #{spec.name} (#{spec.version}), and Bundler cannot continue."
end
def spec_settings
# Fetch the build settings, if there are any
if settings = Bundler.settings["build.#{spec.name}"]
require "shellwords"
Shellwords.shellsplit(settings)
end
end
def install
spec.source.install(
spec,
:force => force,
:ensure_builtin_gems_cached => standalone,
:build_args => Array(spec_settings),
:previous_spec => previous_spec,
)
end
def previous_spec
locked_gems = installer.definition.locked_gems
return unless locked_gems
locked_gems.specs.find {|s| s.name == spec.name }
end
def out_of_space_message
"#{install_error_message}\nYour disk is out of space. Free some space to be able to install your bundle."
end
def generate_executable_stubs
return if Bundler.feature_flag.forget_cli_options?
return if Bundler.settings[:inline]
if Bundler.settings[:bin] && standalone
installer.generate_standalone_bundler_executable_stubs(spec)
elsif Bundler.settings[:bin]
installer.generate_bundler_executable_stubs(spec, :force => true)
end
end
end
end

View File

@@ -1,220 +0,0 @@
# frozen_string_literal: true
require_relative "../worker"
require_relative "gem_installer"
module Bundler
class ParallelInstaller
class SpecInstallation
attr_accessor :spec, :name, :full_name, :post_install_message, :state, :error
def initialize(spec)
@spec = spec
@name = spec.name
@full_name = spec.full_name
@state = :none
@post_install_message = ""
@error = nil
end
def installed?
state == :installed
end
def enqueued?
state == :enqueued
end
def failed?
state == :failed
end
def ready_to_enqueue?
state == :none
end
def has_post_install_message?
!post_install_message.empty?
end
def ignorable_dependency?(dep)
dep.type == :development || dep.name == @name
end
# Checks installed dependencies against spec's dependencies to make
# sure needed dependencies have been installed.
def dependencies_installed?(all_specs)
installed_specs = all_specs.select(&:installed?).map(&:name)
dependencies.all? {|d| installed_specs.include? d.name }
end
# Represents only the non-development dependencies, the ones that are
# itself and are in the total list.
def dependencies
@dependencies ||= all_dependencies.reject {|dep| ignorable_dependency? dep }
end
# Represents all dependencies
def all_dependencies
@spec.dependencies
end
def to_s
"#<#{self.class} #{full_name} (#{state})>"
end
end
def self.call(*args)
new(*args).call
end
attr_reader :size
def initialize(installer, all_specs, size, standalone, force)
@installer = installer
@size = size
@standalone = standalone
@force = force
@specs = all_specs.map {|s| SpecInstallation.new(s) }
@spec_set = all_specs
@rake = @specs.find {|s| s.name == "rake" }
end
def call
if @rake
do_install(@rake, 0)
Gem::Specification.reset
end
if @size > 1
install_with_worker
else
install_serially
end
check_for_unmet_dependencies
handle_error if failed_specs.any?
@specs
ensure
worker_pool&.stop
end
def check_for_unmet_dependencies
unmet_dependencies = @specs.map do |s|
[
s,
s.dependencies.reject {|dep| @specs.any? {|spec| dep.matches_spec?(spec.spec) } },
]
end.reject {|a| a.last.empty? }
return if unmet_dependencies.empty?
warning = []
warning << "Your lockfile doesn't include a valid resolution."
warning << "You can fix this by regenerating your lockfile or manually editing the bad locked gems to a version that satisfies all dependencies."
warning << "The unmet dependencies are:"
unmet_dependencies.each do |spec, unmet_spec_dependencies|
unmet_spec_dependencies.each do |unmet_spec_dependency|
found = @specs.find {|s| s.name == unmet_spec_dependency.name && !unmet_spec_dependency.matches_spec?(s.spec) }
warning << "* #{unmet_spec_dependency}, dependency of #{spec.full_name}, unsatisfied by #{found.full_name}"
end
end
Bundler.ui.warn(warning.join("\n"))
end
private
def failed_specs
@specs.select(&:failed?)
end
def install_with_worker
enqueue_specs
process_specs until finished_installing?
end
def install_serially
until finished_installing?
raise "failed to find a spec to enqueue while installing serially" unless spec_install = @specs.find(&:ready_to_enqueue?)
spec_install.state = :enqueued
do_install(spec_install, 0)
end
end
def worker_pool
@worker_pool ||= Bundler::Worker.new @size, "Parallel Installer", lambda {|spec_install, worker_num|
do_install(spec_install, worker_num)
}
end
def do_install(spec_install, worker_num)
Plugin.hook(Plugin::Events::GEM_BEFORE_INSTALL, spec_install)
gem_installer = Bundler::GemInstaller.new(
spec_install.spec, @installer, @standalone, worker_num, @force
)
success, message = gem_installer.install_from_spec
if success
spec_install.state = :installed
spec_install.post_install_message = message unless message.nil?
else
spec_install.error = "#{message}\n\n#{require_tree_for_spec(spec_install.spec)}"
spec_install.state = :failed
end
Plugin.hook(Plugin::Events::GEM_AFTER_INSTALL, spec_install)
spec_install
end
# Dequeue a spec and save its post-install message and then enqueue the
# remaining specs.
# Some specs might've had to wait til this spec was installed to be
# processed so the call to `enqueue_specs` is important after every
# dequeue.
def process_specs
worker_pool.deq
enqueue_specs
end
def finished_installing?
@specs.all? do |spec|
return true if spec.failed?
spec.installed?
end
end
def handle_error
errors = failed_specs.map(&:error)
if exception = errors.find {|e| e.is_a?(Bundler::BundlerError) }
raise exception
end
raise Bundler::InstallError, errors.join("\n\n")
end
def require_tree_for_spec(spec)
tree = @spec_set.what_required(spec)
t = String.new("In #{File.basename(SharedHelpers.default_gemfile)}:\n")
tree.each_with_index do |s, depth|
t << " " * depth.succ << s.name
unless tree.last == s
t << %( was resolved to #{s.version}, which depends on)
end
t << %(\n)
end
t
end
# Keys in the remains hash represent uninstalled gems specs.
# We enqueue all gem specs that do not have any dependencies.
# Later we call this lambda again to install specs that depended on
# previously installed specifications. We continue until all specs
# are installed.
def enqueue_specs
@specs.select(&:ready_to_enqueue?).each do |spec|
if spec.dependencies_installed? @specs
spec.state = :enqueued
worker_pool.enq spec
end
end
end
end
end

View File

@@ -1,103 +0,0 @@
# frozen_string_literal: true
module Bundler
class Standalone
def initialize(groups, definition)
@specs = definition.specs_for(groups)
end
def generate
SharedHelpers.filesystem_access(bundler_path) do |p|
FileUtils.mkdir_p(p)
end
File.open File.join(bundler_path, "setup.rb"), "w" do |file|
file.puts "require 'rbconfig'"
file.puts define_path_helpers
file.puts reverse_rubygems_kernel_mixin
paths.each do |path|
if Pathname.new(path).absolute?
file.puts %($:.unshift "#{path}")
else
file.puts %($:.unshift File.expand_path("\#{__dir__}/#{path}"))
end
end
end
end
private
def paths
@specs.map do |spec|
next if spec.name == "bundler"
Array(spec.require_paths).map do |path|
gem_path(path, spec).
sub(version_dir, '#{RUBY_ENGINE}/#{Gem.ruby_api_version}').
sub(extensions_dir, 'extensions/\k<platform>/#{Gem.extension_api_version}')
# This is a static string intentionally. It's interpolated at a later time.
end
end.flatten.compact
end
def version_dir
"#{RUBY_ENGINE}/#{Gem.ruby_api_version}"
end
def extensions_dir
%r{extensions/(?<platform>[^/]+)/#{Regexp.escape(Gem.extension_api_version)}}
end
def bundler_path
Bundler.root.join(Bundler.settings[:path].to_s, "bundler")
end
def gem_path(path, spec)
full_path = Pathname.new(path).absolute? ? path : File.join(spec.full_gem_path, path)
if spec.source.instance_of?(Source::Path) && spec.source.path.absolute?
full_path
else
Pathname.new(full_path).relative_path_from(Bundler.root.join(bundler_path)).to_s
end
rescue TypeError
error_message = "#{spec.name} #{spec.version} has an invalid gemspec"
raise Gem::InvalidSpecificationException.new(error_message)
end
def define_path_helpers
<<~'END'
unless defined?(Gem)
module Gem
def self.ruby_api_version
RbConfig::CONFIG["ruby_version"]
end
def self.extension_api_version
if 'no' == RbConfig::CONFIG['ENABLE_SHARED']
"#{ruby_api_version}-static"
else
ruby_api_version
end
end
end
end
END
end
def reverse_rubygems_kernel_mixin
<<~END
if Gem.respond_to?(:discover_gems_on_require=)
Gem.discover_gems_on_require = false
else
kernel = (class << ::Kernel; self; end)
[kernel, ::Kernel].each do |k|
if k.private_method_defined?(:gem_original_require)
private_require = k.private_method_defined?(:require)
k.send(:remove_method, :require)
k.send(:define_method, :require, k.instance_method(:gem_original_require))
k.send(:private, :require) if private_require
end
end
end
END
end
end
end

View File

@@ -1,159 +0,0 @@
# frozen_string_literal: true
require_relative "force_platform"
module Bundler
class LazySpecification
include MatchPlatform
include ForcePlatform
attr_reader :name, :version, :dependencies, :platform
attr_accessor :source, :remote, :force_ruby_platform
def initialize(name, version, platform, source = nil)
@name = name
@version = version
@dependencies = []
@platform = platform || Gem::Platform::RUBY
@source = source
@force_ruby_platform = default_force_ruby_platform
end
def full_name
@full_name ||= if platform == Gem::Platform::RUBY
"#{@name}-#{@version}"
else
"#{@name}-#{@version}-#{platform}"
end
end
def ==(other)
full_name == other.full_name
end
def eql?(other)
full_name.eql?(other.full_name)
end
def hash
full_name.hash
end
##
# Does this locked specification satisfy +dependency+?
#
# NOTE: Rubygems default requirement is ">= 0", which doesn't match
# prereleases of 0 versions, like "0.0.0.dev" or "0.0.0.SNAPSHOT". However,
# bundler users expect those to work. We need to make sure that Gemfile
# dependencies without explicit requirements (which use ">= 0" under the
# hood by default) are still valid for locked specs using this kind of
# versions. The method implements an ad-hoc fix for that. A better solution
# might be to change default rubygems requirement of dependencies to be ">=
# 0.A" but that's a major refactoring likely to break things. Hopefully we
# can attempt it in the future.
#
def satisfies?(dependency)
effective_requirement = dependency.requirement == Gem::Requirement.default ? Gem::Requirement.new(">= 0.A") : dependency.requirement
@name == dependency.name && effective_requirement.satisfied_by?(Gem::Version.new(@version))
end
def to_lock
out = String.new
if platform == Gem::Platform::RUBY
out << " #{name} (#{version})\n"
else
out << " #{name} (#{version}-#{platform})\n"
end
dependencies.sort_by(&:to_s).uniq.each do |dep|
next if dep.type == :development
out << " #{dep.to_lock}\n"
end
out
end
def materialize_for_installation
source.local!
matching_specs = source.specs.search(use_exact_resolved_specifications? ? self : [name, version])
return self if matching_specs.empty?
candidates = if use_exact_resolved_specifications?
matching_specs
else
target_platform = ruby_platform_materializes_to_ruby_platform? ? platform : local_platform
installable_candidates = GemHelpers.select_best_platform_match(matching_specs, target_platform)
specification = __materialize__(installable_candidates, :fallback_to_non_installable => false)
return specification unless specification.nil?
if target_platform != platform
installable_candidates = GemHelpers.select_best_platform_match(matching_specs, platform)
end
installable_candidates
end
__materialize__(candidates)
end
# If in frozen mode, we fallback to a non-installable candidate because by
# doing this we avoid re-resolving and potentially end up changing the
# lock file, which is not allowed. In that case, we will give a proper error
# about the mismatch higher up the stack, right before trying to install the
# bad gem.
def __materialize__(candidates, fallback_to_non_installable: Bundler.frozen_bundle?)
search = candidates.reverse.find do |spec|
spec.is_a?(StubSpecification) ||
(spec.matches_current_ruby? &&
spec.matches_current_rubygems?)
end
if search.nil? && fallback_to_non_installable
search = candidates.last
else
search.dependencies = dependencies if search && search.full_name == full_name && (search.is_a?(RemoteSpecification) || search.is_a?(EndpointSpecification))
end
search
end
def to_s
@to_s ||= if platform == Gem::Platform::RUBY
"#{name} (#{version})"
else
"#{name} (#{version}-#{platform})"
end
end
def git_version
return unless source.is_a?(Bundler::Source::Git)
" #{source.revision[0..6]}"
end
private
def use_exact_resolved_specifications?
@use_exact_resolved_specifications ||= !source.is_a?(Source::Path) && ruby_platform_materializes_to_ruby_platform?
end
#
# For backwards compatibility with existing lockfiles, if the most specific
# locked platform is not a specific platform like x86_64-linux or
# universal-java-11, then we keep the previous behaviour of resolving the
# best platform variant at materiliazation time. For previous bundler
# versions (before 2.2.0) this was always the case (except when the lockfile
# only included non-ruby platforms), but we're also keeping this behaviour
# on newer bundlers unless users generate the lockfile from scratch or
# explicitly add a more specific platform.
#
def ruby_platform_materializes_to_ruby_platform?
generic_platform = generic_local_platform == Gem::Platform::JAVA ? Gem::Platform::JAVA : Gem::Platform::RUBY
!Bundler.most_specific_locked_platform?(generic_platform) || force_ruby_platform || Bundler.settings[:force_ruby_platform]
end
end
end

View File

@@ -1,95 +0,0 @@
# frozen_string_literal: true
module Bundler
class LockfileGenerator
attr_reader :definition
attr_reader :out
# @private
def initialize(definition)
@definition = definition
@out = String.new
end
def self.generate(definition)
new(definition).generate!
end
def generate!
add_sources
add_platforms
add_dependencies
add_locked_ruby_version
add_bundled_with
out
end
private
def add_sources
definition.send(:sources).lock_sources.each_with_index do |source, idx|
out << "\n" unless idx.zero?
# Add the source header
out << source.to_lock
# Find all specs for this source
specs = definition.resolve.select {|s| source.can_lock?(s) }
add_specs(specs)
end
end
def add_specs(specs)
# This needs to be sorted by full name so that
# gems with the same name, but different platform
# are ordered consistently
specs.sort_by(&:full_name).each do |spec|
next if spec.name == "bundler"
out << spec.to_lock
end
end
def add_platforms
add_section("PLATFORMS", definition.platforms)
end
def add_dependencies
out << "\nDEPENDENCIES\n"
handled = []
definition.dependencies.sort_by(&:to_s).each do |dep|
next if handled.include?(dep.name)
out << dep.to_lock << "\n"
handled << dep.name
end
end
def add_locked_ruby_version
return unless locked_ruby_version = definition.locked_ruby_version
add_section("RUBY VERSION", locked_ruby_version.to_s)
end
def add_bundled_with
add_section("BUNDLED WITH", definition.bundler_version_to_lock.to_s)
end
def add_section(name, value)
out << "\n#{name}\n"
case value
when Array
value.map(&:to_s).sort.each do |val|
out << " #{val}\n"
end
when Hash
value.to_a.sort_by {|k, _| k.to_s }.each do |key, val|
out << " #{key}: #{val}\n"
end
when String
out << " #{value}\n"
else
raise ArgumentError, "#{value.inspect} can't be serialized in a lockfile"
end
end
end
end

View File

@@ -1,225 +0,0 @@
# frozen_string_literal: true
module Bundler
class LockfileParser
attr_reader :sources, :dependencies, :specs, :platforms, :bundler_version, :ruby_version
BUNDLED = "BUNDLED WITH"
DEPENDENCIES = "DEPENDENCIES"
PLATFORMS = "PLATFORMS"
RUBY = "RUBY VERSION"
GIT = "GIT"
GEM = "GEM"
PATH = "PATH"
PLUGIN = "PLUGIN SOURCE"
SPECS = " specs:"
OPTIONS = /^ ([a-z]+): (.*)$/i.freeze
SOURCE = [GIT, GEM, PATH, PLUGIN].freeze
SECTIONS_BY_VERSION_INTRODUCED = {
Gem::Version.create("1.0") => [DEPENDENCIES, PLATFORMS, GIT, GEM, PATH].freeze,
Gem::Version.create("1.10") => [BUNDLED].freeze,
Gem::Version.create("1.12") => [RUBY].freeze,
Gem::Version.create("1.13") => [PLUGIN].freeze,
}.freeze
KNOWN_SECTIONS = SECTIONS_BY_VERSION_INTRODUCED.values.flatten.freeze
ENVIRONMENT_VERSION_SECTIONS = [BUNDLED, RUBY].freeze
deprecate_constant(:ENVIRONMENT_VERSION_SECTIONS)
def self.sections_in_lockfile(lockfile_contents)
lockfile_contents.scan(/^\w[\w ]*$/).uniq
end
def self.unknown_sections_in_lockfile(lockfile_contents)
sections_in_lockfile(lockfile_contents) - KNOWN_SECTIONS
end
def self.sections_to_ignore(base_version = nil)
base_version &&= base_version.release
base_version ||= Gem::Version.create("1.0".dup)
attributes = []
SECTIONS_BY_VERSION_INTRODUCED.each do |version, introduced|
next if version <= base_version
attributes += introduced
end
attributes
end
def self.bundled_with
lockfile = Bundler.default_lockfile
return unless lockfile.file?
lockfile_contents = Bundler.read_file(lockfile)
return unless lockfile_contents.include?(BUNDLED)
lockfile_contents.split(BUNDLED).last.strip
end
def initialize(lockfile)
@platforms = []
@sources = []
@dependencies = {}
@state = nil
@specs = {}
if lockfile.match?(/<<<<<<<|=======|>>>>>>>|\|\|\|\|\|\|\|/)
raise LockfileError, "Your #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)} contains merge conflicts.\n" \
"Run `git checkout HEAD -- #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)}` first to get a clean lock."
end
lockfile.split(/(?:\r?\n)+/).each do |line|
if SOURCE.include?(line)
@state = :source
parse_source(line)
elsif line == DEPENDENCIES
@state = :dependency
elsif line == PLATFORMS
@state = :platform
elsif line == RUBY
@state = :ruby
elsif line == BUNDLED
@state = :bundled_with
elsif /^[^\s]/.match?(line)
@state = nil
elsif @state
send("parse_#{@state}", line)
end
end
@specs = @specs.values.sort_by(&:full_name)
rescue ArgumentError => e
Bundler.ui.debug(e)
raise LockfileError, "Your lockfile is unreadable. Run `rm #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)}` " \
"and then `bundle install` to generate a new lockfile."
end
def may_include_redundant_platform_specific_gems?
bundler_version.nil? || bundler_version < Gem::Version.new("1.16.2")
end
private
TYPES = {
GIT => Bundler::Source::Git,
GEM => Bundler::Source::Rubygems,
PATH => Bundler::Source::Path,
PLUGIN => Bundler::Plugin,
}.freeze
def parse_source(line)
case line
when SPECS
case @type
when PATH
@current_source = TYPES[@type].from_lock(@opts)
@sources << @current_source
when GIT
@current_source = TYPES[@type].from_lock(@opts)
@sources << @current_source
when GEM
@opts["remotes"] = Array(@opts.delete("remote")).reverse
@current_source = TYPES[@type].from_lock(@opts)
@sources << @current_source
when PLUGIN
@current_source = Plugin.source_from_lock(@opts)
@sources << @current_source
end
when OPTIONS
value = $2
value = true if value == "true"
value = false if value == "false"
key = $1
if @opts[key]
@opts[key] = Array(@opts[key])
@opts[key] << value
else
@opts[key] = value
end
when *SOURCE
@current_source = nil
@opts = {}
@type = line
else
parse_spec(line)
end
end
space = / /
NAME_VERSION = /
^(#{space}{2}|#{space}{4}|#{space}{6})(?!#{space}) # Exactly 2, 4, or 6 spaces at the start of the line
(.*?) # Name
(?:#{space}\(([^-]*) # Space, followed by version
(?:-(.*))?\))? # Optional platform
(!)? # Optional pinned marker
$ # Line end
/xo.freeze
def parse_dependency(line)
return unless line =~ NAME_VERSION
spaces = $1
return unless spaces.size == 2
name = $2
version = $3
pinned = $5
version = version.split(",").map(&:strip) if version
dep = Bundler::Dependency.new(name, version)
if pinned && dep.name != "bundler"
spec = @specs.find {|_, v| v.name == dep.name }
dep.source = spec.last.source if spec
# Path sources need to know what the default name / version
# to use in the case that there are no gemspecs present. A fake
# gemspec is created based on the version set on the dependency
# TODO: Use the version from the spec instead of from the dependency
if version && version.size == 1 && version.first =~ /^\s*= (.+)\s*$/ && dep.source.is_a?(Bundler::Source::Path)
dep.source.name = name
dep.source.version = $1
end
end
@dependencies[dep.name] = dep
end
def parse_spec(line)
return unless line =~ NAME_VERSION
spaces = $1
name = $2
version = $3
platform = $4
if spaces.size == 4
version = Gem::Version.new(version)
platform = platform ? Gem::Platform.new(platform) : Gem::Platform::RUBY
@current_spec = LazySpecification.new(name, version, platform)
@current_spec.source = @current_source
@current_source.add_dependency_names(name)
@specs[@current_spec.full_name] = @current_spec
elsif spaces.size == 6
version = version.split(",").map(&:strip) if version
dep = Gem::Dependency.new(name, version)
@current_spec.dependencies << dep
end
end
def parse_platform(line)
@platforms << Gem::Platform.new($1) if line =~ /^ (.*)$/
end
def parse_bundled_with(line)
line = line.strip
return unless Gem::Version.correct?(line)
@bundler_version = Gem::Version.create(line)
end
def parse_ruby(line)
@ruby_version = line.strip
end
end
end

View File

@@ -1,82 +0,0 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "BUNDLE\-ADD" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-add\fR \- Add gem to the Gemfile and run bundle install
.
.SH "SYNOPSIS"
\fBbundle add\fR \fIGEM_NAME\fR [\-\-group=GROUP] [\-\-version=VERSION] [\-\-source=SOURCE] [\-\-path=PATH] [\-\-git=GIT] [\-\-github=GITHUB] [\-\-branch=BRANCH] [\-\-ref=REF] [\-\-skip\-install] [\-\-strict] [\-\-optimistic]
.
.SH "DESCRIPTION"
Adds the named gem to the Gemfile and run \fBbundle install\fR\. \fBbundle install\fR can be avoided by using the flag \fB\-\-skip\-install\fR\.
.
.P
Example:
.
.P
bundle add rails
.
.P
bundle add rails \-\-version "< 3\.0, > 1\.1"
.
.P
bundle add rails \-\-version "~> 5\.0\.0" \-\-source "https://gems\.example\.com" \-\-group "development"
.
.P
bundle add rails \-\-skip\-install
.
.P
bundle add rails \-\-group "development, test"
.
.SH "OPTIONS"
.
.TP
\fB\-\-version\fR, \fB\-v\fR
Specify version requirements(s) for the added gem\.
.
.TP
\fB\-\-group\fR, \fB\-g\fR
Specify the group(s) for the added gem\. Multiple groups should be separated by commas\.
.
.TP
\fB\-\-source\fR, \fB\-s\fR
Specify the source for the added gem\.
.
.TP
\fB\-\-require\fR, \fB\-r\fR
Adds require path to gem\. Provide false, or a path as a string\.
.
.TP
\fB\-\-path\fR
Specify the file system path for the added gem\.
.
.TP
\fB\-\-git\fR
Specify the git source for the added gem\.
.
.TP
\fB\-\-github\fR
Specify the github source for the added gem\.
.
.TP
\fB\-\-branch\fR
Specify the git branch for the added gem\.
.
.TP
\fB\-\-ref\fR
Specify the git ref for the added gem\.
.
.TP
\fB\-\-skip\-install\fR
Adds the gem to the Gemfile but does not install it\.
.
.TP
\fB\-\-optimistic\fR
Adds optimistic declaration of version\.
.
.TP
\fB\-\-strict\fR
Adds strict declaration of version\.

View File

@@ -1,58 +0,0 @@
bundle-add(1) -- Add gem to the Gemfile and run bundle install
================================================================
## SYNOPSIS
`bundle add` <GEM_NAME> [--group=GROUP] [--version=VERSION] [--source=SOURCE] [--path=PATH] [--git=GIT] [--github=GITHUB] [--branch=BRANCH] [--ref=REF] [--skip-install] [--strict] [--optimistic]
## DESCRIPTION
Adds the named gem to the Gemfile and run `bundle install`. `bundle install` can be avoided by using the flag `--skip-install`.
Example:
bundle add rails
bundle add rails --version "< 3.0, > 1.1"
bundle add rails --version "~> 5.0.0" --source "https://gems.example.com" --group "development"
bundle add rails --skip-install
bundle add rails --group "development, test"
## OPTIONS
* `--version`, `-v`:
Specify version requirements(s) for the added gem.
* `--group`, `-g`:
Specify the group(s) for the added gem. Multiple groups should be separated by commas.
* `--source`, `-s`:
Specify the source for the added gem.
* `--require`, `-r`:
Adds require path to gem. Provide false, or a path as a string.
* `--path`:
Specify the file system path for the added gem.
* `--git`:
Specify the git source for the added gem.
* `--github`:
Specify the github source for the added gem.
* `--branch`:
Specify the git branch for the added gem.
* `--ref`:
Specify the git ref for the added gem.
* `--skip-install`:
Adds the gem to the Gemfile but does not install it.
* `--optimistic`:
Adds optimistic declaration of version.
* `--strict`:
Adds strict declaration of version.

View File

@@ -1,42 +0,0 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "BUNDLE\-BINSTUBS" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-binstubs\fR \- Install the binstubs of the listed gems
.
.SH "SYNOPSIS"
\fBbundle binstubs\fR \fIGEM_NAME\fR [\-\-force] [\-\-path PATH] [\-\-standalone]
.
.SH "DESCRIPTION"
Binstubs are scripts that wrap around executables\. Bundler creates a small Ruby file (a binstub) that loads Bundler, runs the command, and puts it into \fBbin/\fR\. Binstubs are a shortcut\-or alternative\- to always using \fBbundle exec\fR\. This gives you a file that can be run directly, and one that will always run the correct gem version used by the application\.
.
.P
For example, if you run \fBbundle binstubs rspec\-core\fR, Bundler will create the file \fBbin/rspec\fR\. That file will contain enough code to load Bundler, tell it to load the bundled gems, and then run rspec\.
.
.P
This command generates binstubs for executables in \fBGEM_NAME\fR\. Binstubs are put into \fBbin\fR, or the \fB\-\-path\fR directory if one has been set\. Calling binstubs with [GEM [GEM]] will create binstubs for all given gems\.
.
.SH "OPTIONS"
.
.TP
\fB\-\-force\fR
Overwrite existing binstubs if they exist\.
.
.TP
\fB\-\-path\fR
The location to install the specified binstubs to\. This defaults to \fBbin\fR\.
.
.TP
\fB\-\-standalone\fR
Makes binstubs that can work without depending on Rubygems or Bundler at runtime\.
.
.TP
\fB\-\-shebang\fR
Specify a different shebang executable name than the default (default \'ruby\')
.
.TP
\fB\-\-all\fR
Create binstubs for all gems in the bundle\.

View File

@@ -1,41 +0,0 @@
bundle-binstubs(1) -- Install the binstubs of the listed gems
=============================================================
## SYNOPSIS
`bundle binstubs` <GEM_NAME> [--force] [--path PATH] [--standalone]
## DESCRIPTION
Binstubs are scripts that wrap around executables. Bundler creates a
small Ruby file (a binstub) that loads Bundler, runs the command,
and puts it into `bin/`. Binstubs are a shortcut-or alternative-
to always using `bundle exec`. This gives you a file that can be run
directly, and one that will always run the correct gem version
used by the application.
For example, if you run `bundle binstubs rspec-core`, Bundler will create
the file `bin/rspec`. That file will contain enough code to load Bundler,
tell it to load the bundled gems, and then run rspec.
This command generates binstubs for executables in `GEM_NAME`.
Binstubs are put into `bin`, or the `--path` directory if one has been set.
Calling binstubs with [GEM [GEM]] will create binstubs for all given gems.
## OPTIONS
* `--force`:
Overwrite existing binstubs if they exist.
* `--path`:
The location to install the specified binstubs to. This defaults to `bin`.
* `--standalone`:
Makes binstubs that can work without depending on Rubygems or Bundler at
runtime.
* `--shebang`:
Specify a different shebang executable name than the default (default 'ruby')
* `--all`:
Create binstubs for all gems in the bundle.

View File

@@ -1,61 +0,0 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "BUNDLE\-CACHE" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-cache\fR \- Package your needed \fB\.gem\fR files into your application
.
.SH "SYNOPSIS"
\fBbundle cache\fR
.
.P
alias: \fBpackage\fR, \fBpack\fR
.
.SH "DESCRIPTION"
Copy all of the \fB\.gem\fR files needed to run the application into the \fBvendor/cache\fR directory\. In the future, when running \fBbundle install(1)\fR \fIbundle\-install\.1\.html\fR, use the gems in the cache in preference to the ones on \fBrubygems\.org\fR\.
.
.SH "GIT AND PATH GEMS"
The \fBbundle cache\fR command can also package \fB:git\fR and \fB:path\fR dependencies besides \.gem files\. This needs to be explicitly enabled via the \fB\-\-all\fR option\. Once used, the \fB\-\-all\fR option will be remembered\.
.
.SH "SUPPORT FOR MULTIPLE PLATFORMS"
When using gems that have different packages for different platforms, Bundler supports caching of gems for other platforms where the Gemfile has been resolved (i\.e\. present in the lockfile) in \fBvendor/cache\fR\. This needs to be enabled via the \fB\-\-all\-platforms\fR option\. This setting will be remembered in your local bundler configuration\.
.
.SH "REMOTE FETCHING"
By default, if you run \fBbundle install(1)\fR \fIbundle\-install\.1\.html\fR after running bundle cache(1) \fIbundle\-cache\.1\.html\fR, bundler will still connect to \fBrubygems\.org\fR to check whether a platform\-specific gem exists for any of the gems in \fBvendor/cache\fR\.
.
.P
For instance, consider this Gemfile(5):
.
.IP "" 4
.
.nf
source "https://rubygems\.org"
gem "nokogiri"
.
.fi
.
.IP "" 0
.
.P
If you run \fBbundle cache\fR under C Ruby, bundler will retrieve the version of \fBnokogiri\fR for the \fB"ruby"\fR platform\. If you deploy to JRuby and run \fBbundle install\fR, bundler is forced to check to see whether a \fB"java"\fR platformed \fBnokogiri\fR exists\.
.
.P
Even though the \fBnokogiri\fR gem for the Ruby platform is \fItechnically\fR acceptable on JRuby, it has a C extension that does not run on JRuby\. As a result, bundler will, by default, still connect to \fBrubygems\.org\fR to check whether it has a version of one of your gems more specific to your platform\.
.
.P
This problem is also not limited to the \fB"java"\fR platform\. A similar (common) problem can happen when developing on Windows and deploying to Linux, or even when developing on OSX and deploying to Linux\.
.
.P
If you know for sure that the gems packaged in \fBvendor/cache\fR are appropriate for the platform you are on, you can run \fBbundle install \-\-local\fR to skip checking for more appropriate gems, and use the ones in \fBvendor/cache\fR\.
.
.P
One way to be sure that you have the right platformed versions of all your gems is to run \fBbundle cache\fR on an identical machine and check in the gems\. For instance, you can run \fBbundle cache\fR on an identical staging box during your staging process, and check in the \fBvendor/cache\fR before deploying to production\.
.
.P
By default, bundle cache(1) \fIbundle\-cache\.1\.html\fR fetches and also installs the gems to the default location\. To package the dependencies to \fBvendor/cache\fR without installing them to the local install location, you can run \fBbundle cache \-\-no\-install\fR\.
.
.SH "HISTORY"
In Bundler 2\.1, \fBcache\fR took in the functionalities of \fBpackage\fR and now \fBpackage\fR and \fBpack\fR are aliases of \fBcache\fR\.

View File

@@ -1,79 +0,0 @@
bundle-cache(1) -- Package your needed `.gem` files into your application
===========================================================================
## SYNOPSIS
`bundle cache`
alias: `package`, `pack`
## DESCRIPTION
Copy all of the `.gem` files needed to run the application into the
`vendor/cache` directory. In the future, when running [`bundle install(1)`](bundle-install.1.html),
use the gems in the cache in preference to the ones on `rubygems.org`.
## GIT AND PATH GEMS
The `bundle cache` command can also package `:git` and `:path` dependencies
besides .gem files. This needs to be explicitly enabled via the `--all` option.
Once used, the `--all` option will be remembered.
## SUPPORT FOR MULTIPLE PLATFORMS
When using gems that have different packages for different platforms, Bundler
supports caching of gems for other platforms where the Gemfile has been resolved
(i.e. present in the lockfile) in `vendor/cache`. This needs to be enabled via
the `--all-platforms` option. This setting will be remembered in your local
bundler configuration.
## REMOTE FETCHING
By default, if you run [`bundle install(1)`](bundle-install.1.html) after running
[bundle cache(1)](bundle-cache.1.html), bundler will still connect to `rubygems.org`
to check whether a platform-specific gem exists for any of the gems
in `vendor/cache`.
For instance, consider this Gemfile(5):
source "https://rubygems.org"
gem "nokogiri"
If you run `bundle cache` under C Ruby, bundler will retrieve
the version of `nokogiri` for the `"ruby"` platform. If you deploy
to JRuby and run `bundle install`, bundler is forced to check to
see whether a `"java"` platformed `nokogiri` exists.
Even though the `nokogiri` gem for the Ruby platform is
_technically_ acceptable on JRuby, it has a C extension
that does not run on JRuby. As a result, bundler will, by default,
still connect to `rubygems.org` to check whether it has a version
of one of your gems more specific to your platform.
This problem is also not limited to the `"java"` platform.
A similar (common) problem can happen when developing on Windows
and deploying to Linux, or even when developing on OSX and
deploying to Linux.
If you know for sure that the gems packaged in `vendor/cache`
are appropriate for the platform you are on, you can run
`bundle install --local` to skip checking for more appropriate
gems, and use the ones in `vendor/cache`.
One way to be sure that you have the right platformed versions
of all your gems is to run `bundle cache` on an identical
machine and check in the gems. For instance, you can run
`bundle cache` on an identical staging box during your
staging process, and check in the `vendor/cache` before
deploying to production.
By default, [bundle cache(1)](bundle-cache.1.html) fetches and also
installs the gems to the default location. To package the
dependencies to `vendor/cache` without installing them to the
local install location, you can run `bundle cache --no-install`.
## HISTORY
In Bundler 2.1, `cache` took in the functionalities of `package` and now
`package` and `pack` are aliases of `cache`.

View File

@@ -1,31 +0,0 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "BUNDLE\-CHECK" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-check\fR \- Verifies if dependencies are satisfied by installed gems
.
.SH "SYNOPSIS"
\fBbundle check\fR [\-\-dry\-run] [\-\-gemfile=FILE] [\-\-path=PATH]
.
.SH "DESCRIPTION"
\fBcheck\fR searches the local machine for each of the gems requested in the Gemfile\. If all gems are found, Bundler prints a success message and exits with a status of 0\.
.
.P
If not, the first missing gem is listed and Bundler exits status 1\.
.
.SH "OPTIONS"
.
.TP
\fB\-\-dry\-run\fR
Locks the [\fBGemfile(5)\fR][Gemfile(5)] before running the command\.
.
.TP
\fB\-\-gemfile\fR
Use the specified gemfile instead of the [\fBGemfile(5)\fR][Gemfile(5)]\.
.
.TP
\fB\-\-path\fR
Specify a different path than the system default (\fB$BUNDLE_PATH\fR or \fB$GEM_HOME\fR)\. Bundler will remember this value for future installs on this machine\.

View File

@@ -1,26 +0,0 @@
bundle-check(1) -- Verifies if dependencies are satisfied by installed gems
===========================================================================
## SYNOPSIS
`bundle check` [--dry-run]
[--gemfile=FILE]
[--path=PATH]
## DESCRIPTION
`check` searches the local machine for each of the gems requested in the
Gemfile. If all gems are found, Bundler prints a success message and exits with
a status of 0.
If not, the first missing gem is listed and Bundler exits status 1.
## OPTIONS
* `--dry-run`:
Locks the [`Gemfile(5)`][Gemfile(5)] before running the command.
* `--gemfile`:
Use the specified gemfile instead of the [`Gemfile(5)`][Gemfile(5)].
* `--path`:
Specify a different path than the system default (`$BUNDLE_PATH` or `$GEM_HOME`).
Bundler will remember this value for future installs on this machine.

View File

@@ -1,24 +0,0 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "BUNDLE\-CLEAN" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-clean\fR \- Cleans up unused gems in your bundler directory
.
.SH "SYNOPSIS"
\fBbundle clean\fR [\-\-dry\-run] [\-\-force]
.
.SH "DESCRIPTION"
This command will remove all unused gems in your bundler directory\. This is useful when you have made many changes to your gem dependencies\.
.
.SH "OPTIONS"
.
.TP
\fB\-\-dry\-run\fR
Print the changes, but do not clean the unused gems\.
.
.TP
\fB\-\-force\fR
Forces cleaning up unused gems even if Bundler is configured to use globally installed gems\. As a consequence, removes all system gems except for the ones in the current application\.

View File

@@ -1,18 +0,0 @@
bundle-clean(1) -- Cleans up unused gems in your bundler directory
==================================================================
## SYNOPSIS
`bundle clean` [--dry-run] [--force]
## DESCRIPTION
This command will remove all unused gems in your bundler directory. This is
useful when you have made many changes to your gem dependencies.
## OPTIONS
* `--dry-run`:
Print the changes, but do not clean the unused gems.
* `--force`:
Forces cleaning up unused gems even if Bundler is configured to use globally installed gems. As a consequence, removes all system gems except for the ones in the current application.

View File

@@ -1,512 +0,0 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "BUNDLE\-CONFIG" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-config\fR \- Set bundler configuration options
.
.SH "SYNOPSIS"
\fBbundle config\fR list
.
.br
\fBbundle config\fR [get] NAME
.
.br
\fBbundle config\fR [set] NAME VALUE
.
.br
\fBbundle config\fR unset NAME
.
.SH "DESCRIPTION"
This command allows you to interact with Bundler\'s configuration system\.
.
.P
Bundler loads configuration settings in this order:
.
.IP "1." 4
Local config (\fB<project_root>/\.bundle/config\fR or \fB$BUNDLE_APP_CONFIG/config\fR)
.
.IP "2." 4
Environmental variables (\fBENV\fR)
.
.IP "3." 4
Global config (\fB~/\.bundle/config\fR)
.
.IP "4." 4
Bundler default config
.
.IP "" 0
.
.P
Executing \fBbundle config list\fR will print a list of all bundler configuration for the current bundle, and where that configuration was set\.
.
.P
Executing \fBbundle config get <name>\fR will print the value of that configuration setting, and where it was set\.
.
.P
Executing \fBbundle config set <name> <value>\fR defaults to setting \fBlocal\fR configuration if executing from within a local application, otherwise it will set \fBglobal\fR configuration\. See \fB\-\-local\fR and \fB\-\-global\fR options below\.
.
.P
Executing \fBbundle config set \-\-local <name> <value>\fR will set that configuration in the directory for the local application\. The configuration will be stored in \fB<project_root>/\.bundle/config\fR\. If \fBBUNDLE_APP_CONFIG\fR is set, the configuration will be stored in \fB$BUNDLE_APP_CONFIG/config\fR\.
.
.P
Executing \fBbundle config set \-\-global <name> <value>\fR will set that configuration to the value specified for all bundles executed as the current user\. The configuration will be stored in \fB~/\.bundle/config\fR\. If \fIname\fR already is set, \fIname\fR will be overridden and user will be warned\.
.
.P
Executing \fBbundle config unset <name>\fR will delete the configuration in both local and global sources\.
.
.P
Executing \fBbundle config unset \-\-global <name>\fR will delete the configuration only from the user configuration\.
.
.P
Executing \fBbundle config unset \-\-local <name>\fR will delete the configuration only from the local application\.
.
.P
Executing bundle with the \fBBUNDLE_IGNORE_CONFIG\fR environment variable set will cause it to ignore all configuration\.
.
.SH "REMEMBERING OPTIONS"
Flags passed to \fBbundle install\fR or the Bundler runtime, such as \fB\-\-path foo\fR or \fB\-\-without production\fR, are remembered between commands and saved to your local application\'s configuration (normally, \fB\./\.bundle/config\fR)\.
.
.P
However, this will be changed in bundler 3, so it\'s better not to rely on this behavior\. If these options must be remembered, it\'s better to set them using \fBbundle config\fR (e\.g\., \fBbundle config set \-\-local path foo\fR)\.
.
.P
The options that can be configured are:
.
.TP
\fBbin\fR
Creates a directory (defaults to \fB~/bin\fR) and place any executables from the gem there\. These executables run in Bundler\'s context\. If used, you might add this directory to your environment\'s \fBPATH\fR variable\. For instance, if the \fBrails\fR gem comes with a \fBrails\fR executable, this flag will create a \fBbin/rails\fR executable that ensures that all referred dependencies will be resolved using the bundled gems\.
.
.TP
\fBdeployment\fR
In deployment mode, Bundler will \'roll\-out\' the bundle for \fBproduction\fR use\. Please check carefully if you want to have this option enabled in \fBdevelopment\fR or \fBtest\fR environments\.
.
.TP
\fBonly\fR
A space\-separated list of groups to install only gems of the specified groups\.
.
.TP
\fBpath\fR
The location to install the specified gems to\. This defaults to Rubygems\' setting\. Bundler shares this location with Rubygems, \fBgem install \.\.\.\fR will have gem installed there, too\. Therefore, gems installed without a \fB\-\-path \.\.\.\fR setting will show up by calling \fBgem list\fR\. Accordingly, gems installed to other locations will not get listed\.
.
.TP
\fBwithout\fR
A space\-separated list of groups referencing gems to skip during installation\.
.
.TP
\fBwith\fR
A space\-separated list of \fBoptional\fR groups referencing gems to include during installation\.
.
.SH "BUILD OPTIONS"
You can use \fBbundle config\fR to give Bundler the flags to pass to the gem installer every time bundler tries to install a particular gem\.
.
.P
A very common example, the \fBmysql\fR gem, requires Snow Leopard users to pass configuration flags to \fBgem install\fR to specify where to find the \fBmysql_config\fR executable\.
.
.IP "" 4
.
.nf
gem install mysql \-\- \-\-with\-mysql\-config=/usr/local/mysql/bin/mysql_config
.
.fi
.
.IP "" 0
.
.P
Since the specific location of that executable can change from machine to machine, you can specify these flags on a per\-machine basis\.
.
.IP "" 4
.
.nf
bundle config set \-\-global build\.mysql \-\-with\-mysql\-config=/usr/local/mysql/bin/mysql_config
.
.fi
.
.IP "" 0
.
.P
After running this command, every time bundler needs to install the \fBmysql\fR gem, it will pass along the flags you specified\.
.
.SH "CONFIGURATION KEYS"
Configuration keys in bundler have two forms: the canonical form and the environment variable form\.
.
.P
For instance, passing the \fB\-\-without\fR flag to bundle install(1) \fIbundle\-install\.1\.html\fR prevents Bundler from installing certain groups specified in the Gemfile(5)\. Bundler persists this value in \fBapp/\.bundle/config\fR so that calls to \fBBundler\.setup\fR do not try to find gems from the \fBGemfile\fR that you didn\'t install\. Additionally, subsequent calls to bundle install(1) \fIbundle\-install\.1\.html\fR remember this setting and skip those groups\.
.
.P
The canonical form of this configuration is \fB"without"\fR\. To convert the canonical form to the environment variable form, capitalize it, and prepend \fBBUNDLE_\fR\. The environment variable form of \fB"without"\fR is \fBBUNDLE_WITHOUT\fR\.
.
.P
Any periods in the configuration keys must be replaced with two underscores when setting it via environment variables\. The configuration key \fBlocal\.rack\fR becomes the environment variable \fBBUNDLE_LOCAL__RACK\fR\.
.
.SH "LIST OF AVAILABLE KEYS"
The following is a list of all configuration keys and their purpose\. You can learn more about their operation in bundle install(1) \fIbundle\-install\.1\.html\fR\.
.
.IP "\(bu" 4
\fBallow_deployment_source_credential_changes\fR (\fBBUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES\fR): When in deployment mode, allow changing the credentials to a gem\'s source\. Ex: \fBhttps://some\.host\.com/gems/path/\fR \-> \fBhttps://user_name:password@some\.host\.com/gems/path\fR
.
.IP "\(bu" 4
\fBallow_offline_install\fR (\fBBUNDLE_ALLOW_OFFLINE_INSTALL\fR): Allow Bundler to use cached data when installing without network access\.
.
.IP "\(bu" 4
\fBauto_clean_without_path\fR (\fBBUNDLE_AUTO_CLEAN_WITHOUT_PATH\fR): Automatically run \fBbundle clean\fR after installing when an explicit \fBpath\fR has not been set and Bundler is not installing into the system gems\.
.
.IP "\(bu" 4
\fBauto_install\fR (\fBBUNDLE_AUTO_INSTALL\fR): Automatically run \fBbundle install\fR when gems are missing\.
.
.IP "\(bu" 4
\fBbin\fR (\fBBUNDLE_BIN\fR): Install executables from gems in the bundle to the specified directory\. Defaults to \fBfalse\fR\.
.
.IP "\(bu" 4
\fBcache_all\fR (\fBBUNDLE_CACHE_ALL\fR): Cache all gems, including path and git gems\. This needs to be explicitly configured on bundler 1 and bundler 2, but will be the default on bundler 3\.
.
.IP "\(bu" 4
\fBcache_all_platforms\fR (\fBBUNDLE_CACHE_ALL_PLATFORMS\fR): Cache gems for all platforms\.
.
.IP "\(bu" 4
\fBcache_path\fR (\fBBUNDLE_CACHE_PATH\fR): The directory that bundler will place cached gems in when running \fBbundle package\fR, and that bundler will look in when installing gems\. Defaults to \fBvendor/cache\fR\.
.
.IP "\(bu" 4
\fBclean\fR (\fBBUNDLE_CLEAN\fR): Whether Bundler should run \fBbundle clean\fR automatically after \fBbundle install\fR\.
.
.IP "\(bu" 4
\fBconsole\fR (\fBBUNDLE_CONSOLE\fR): The console that \fBbundle console\fR starts\. Defaults to \fBirb\fR\.
.
.IP "\(bu" 4
\fBdefault_install_uses_path\fR (\fBBUNDLE_DEFAULT_INSTALL_USES_PATH\fR): Whether a \fBbundle install\fR without an explicit \fB\-\-path\fR argument defaults to installing gems in \fB\.bundle\fR\.
.
.IP "\(bu" 4
\fBdeployment\fR (\fBBUNDLE_DEPLOYMENT\fR): Disallow changes to the \fBGemfile\fR\. When the \fBGemfile\fR is changed and the lockfile has not been updated, running Bundler commands will be blocked\.
.
.IP "\(bu" 4
\fBdisable_checksum_validation\fR (\fBBUNDLE_DISABLE_CHECKSUM_VALIDATION\fR): Allow installing gems even if they do not match the checksum provided by RubyGems\.
.
.IP "\(bu" 4
\fBdisable_exec_load\fR (\fBBUNDLE_DISABLE_EXEC_LOAD\fR): Stop Bundler from using \fBload\fR to launch an executable in\-process in \fBbundle exec\fR\.
.
.IP "\(bu" 4
\fBdisable_local_branch_check\fR (\fBBUNDLE_DISABLE_LOCAL_BRANCH_CHECK\fR): Allow Bundler to use a local git override without a branch specified in the Gemfile\.
.
.IP "\(bu" 4
\fBdisable_local_revision_check\fR (\fBBUNDLE_DISABLE_LOCAL_REVISION_CHECK\fR): Allow Bundler to use a local git override without checking if the revision present in the lockfile is present in the repository\.
.
.IP "\(bu" 4
\fBdisable_shared_gems\fR (\fBBUNDLE_DISABLE_SHARED_GEMS\fR): Stop Bundler from accessing gems installed to RubyGems\' normal location\.
.
.IP "\(bu" 4
\fBdisable_version_check\fR (\fBBUNDLE_DISABLE_VERSION_CHECK\fR): Stop Bundler from checking if a newer Bundler version is available on rubygems\.org\.
.
.IP "\(bu" 4
\fBforce_ruby_platform\fR (\fBBUNDLE_FORCE_RUBY_PLATFORM\fR): Ignore the current machine\'s platform and install only \fBruby\fR platform gems\. As a result, gems with native extensions will be compiled from source\.
.
.IP "\(bu" 4
\fBfrozen\fR (\fBBUNDLE_FROZEN\fR): Disallow changes to the \fBGemfile\fR\. When the \fBGemfile\fR is changed and the lockfile has not been updated, running Bundler commands will be blocked\. Defaults to \fBtrue\fR when \fB\-\-deployment\fR is used\.
.
.IP "\(bu" 4
\fBgem\.github_username\fR (\fBBUNDLE_GEM__GITHUB_USERNAME\fR): Sets a GitHub username or organization to be used in \fBREADME\fR file when you create a new gem via \fBbundle gem\fR command\. It can be overridden by passing an explicit \fB\-\-github\-username\fR flag to \fBbundle gem\fR\.
.
.IP "\(bu" 4
\fBgem\.push_key\fR (\fBBUNDLE_GEM__PUSH_KEY\fR): Sets the \fB\-\-key\fR parameter for \fBgem push\fR when using the \fBrake release\fR command with a private gemstash server\.
.
.IP "\(bu" 4
\fBgemfile\fR (\fBBUNDLE_GEMFILE\fR): The name of the file that bundler should use as the \fBGemfile\fR\. This location of this file also sets the root of the project, which is used to resolve relative paths in the \fBGemfile\fR, among other things\. By default, bundler will search up from the current working directory until it finds a \fBGemfile\fR\.
.
.IP "\(bu" 4
\fBglobal_gem_cache\fR (\fBBUNDLE_GLOBAL_GEM_CACHE\fR): Whether Bundler should cache all gems globally, rather than locally to the installing Ruby installation\.
.
.IP "\(bu" 4
\fBignore_funding_requests\fR (\fBBUNDLE_IGNORE_FUNDING_REQUESTS\fR): When set, no funding requests will be printed\.
.
.IP "\(bu" 4
\fBignore_messages\fR (\fBBUNDLE_IGNORE_MESSAGES\fR): When set, no post install messages will be printed\. To silence a single gem, use dot notation like \fBignore_messages\.httparty true\fR\.
.
.IP "\(bu" 4
\fBinit_gems_rb\fR (\fBBUNDLE_INIT_GEMS_RB\fR): Generate a \fBgems\.rb\fR instead of a \fBGemfile\fR when running \fBbundle init\fR\.
.
.IP "\(bu" 4
\fBjobs\fR (\fBBUNDLE_JOBS\fR): The number of gems Bundler can install in parallel\. Defaults to the number of available processors\.
.
.IP "\(bu" 4
\fBno_install\fR (\fBBUNDLE_NO_INSTALL\fR): Whether \fBbundle package\fR should skip installing gems\.
.
.IP "\(bu" 4
\fBno_prune\fR (\fBBUNDLE_NO_PRUNE\fR): Whether Bundler should leave outdated gems unpruned when caching\.
.
.IP "\(bu" 4
\fBonly\fR (\fBBUNDLE_ONLY\fR): A space\-separated list of groups to install only gems of the specified groups\.
.
.IP "\(bu" 4
\fBpath\fR (\fBBUNDLE_PATH\fR): The location on disk where all gems in your bundle will be located regardless of \fB$GEM_HOME\fR or \fB$GEM_PATH\fR values\. Bundle gems not found in this location will be installed by \fBbundle install\fR\. Defaults to \fBGem\.dir\fR\. When \-\-deployment is used, defaults to vendor/bundle\.
.
.IP "\(bu" 4
\fBpath\.system\fR (\fBBUNDLE_PATH__SYSTEM\fR): Whether Bundler will install gems into the default system path (\fBGem\.dir\fR)\.
.
.IP "\(bu" 4
\fBpath_relative_to_cwd\fR (\fBBUNDLE_PATH_RELATIVE_TO_CWD\fR) Makes \fB\-\-path\fR relative to the CWD instead of the \fBGemfile\fR\.
.
.IP "\(bu" 4
\fBplugins\fR (\fBBUNDLE_PLUGINS\fR): Enable Bundler\'s experimental plugin system\.
.
.IP "\(bu" 4
\fBprefer_patch\fR (BUNDLE_PREFER_PATCH): Prefer updating only to next patch version during updates\. Makes \fBbundle update\fR calls equivalent to \fBbundler update \-\-patch\fR\.
.
.IP "\(bu" 4
\fBprint_only_version_number\fR (\fBBUNDLE_PRINT_ONLY_VERSION_NUMBER\fR): Print only version number from \fBbundler \-\-version\fR\.
.
.IP "\(bu" 4
\fBredirect\fR (\fBBUNDLE_REDIRECT\fR): The number of redirects allowed for network requests\. Defaults to \fB5\fR\.
.
.IP "\(bu" 4
\fBretry\fR (\fBBUNDLE_RETRY\fR): The number of times to retry failed network requests\. Defaults to \fB3\fR\.
.
.IP "\(bu" 4
\fBsetup_makes_kernel_gem_public\fR (\fBBUNDLE_SETUP_MAKES_KERNEL_GEM_PUBLIC\fR): Have \fBBundler\.setup\fR make the \fBKernel#gem\fR method public, even though RubyGems declares it as private\.
.
.IP "\(bu" 4
\fBshebang\fR (\fBBUNDLE_SHEBANG\fR): The program name that should be invoked for generated binstubs\. Defaults to the ruby install name used to generate the binstub\.
.
.IP "\(bu" 4
\fBsilence_deprecations\fR (\fBBUNDLE_SILENCE_DEPRECATIONS\fR): Whether Bundler should silence deprecation warnings for behavior that will be changed in the next major version\.
.
.IP "\(bu" 4
\fBsilence_root_warning\fR (\fBBUNDLE_SILENCE_ROOT_WARNING\fR): Silence the warning Bundler prints when installing gems as root\.
.
.IP "\(bu" 4
\fBssl_ca_cert\fR (\fBBUNDLE_SSL_CA_CERT\fR): Path to a designated CA certificate file or folder containing multiple certificates for trusted CAs in PEM format\.
.
.IP "\(bu" 4
\fBssl_client_cert\fR (\fBBUNDLE_SSL_CLIENT_CERT\fR): Path to a designated file containing a X\.509 client certificate and key in PEM format\.
.
.IP "\(bu" 4
\fBssl_verify_mode\fR (\fBBUNDLE_SSL_VERIFY_MODE\fR): The SSL verification mode Bundler uses when making HTTPS requests\. Defaults to verify peer\.
.
.IP "\(bu" 4
\fBsystem_bindir\fR (\fBBUNDLE_SYSTEM_BINDIR\fR): The location where RubyGems installs binstubs\. Defaults to \fBGem\.bindir\fR\.
.
.IP "\(bu" 4
\fBtimeout\fR (\fBBUNDLE_TIMEOUT\fR): The seconds allowed before timing out for network requests\. Defaults to \fB10\fR\.
.
.IP "\(bu" 4
\fBupdate_requires_all_flag\fR (\fBBUNDLE_UPDATE_REQUIRES_ALL_FLAG\fR): Require passing \fB\-\-all\fR to \fBbundle update\fR when everything should be updated, and disallow passing no options to \fBbundle update\fR\.
.
.IP "\(bu" 4
\fBuser_agent\fR (\fBBUNDLE_USER_AGENT\fR): The custom user agent fragment Bundler includes in API requests\.
.
.IP "\(bu" 4
\fBwith\fR (\fBBUNDLE_WITH\fR): A \fB:\fR\-separated list of groups whose gems bundler should install\.
.
.IP "\(bu" 4
\fBwithout\fR (\fBBUNDLE_WITHOUT\fR): A \fB:\fR\-separated list of groups whose gems bundler should not install\.
.
.IP "" 0
.
.P
In general, you should set these settings per\-application by using the applicable flag to the bundle install(1) \fIbundle\-install\.1\.html\fR or bundle cache(1) \fIbundle\-cache\.1\.html\fR command\.
.
.P
You can set them globally either via environment variables or \fBbundle config\fR, whichever is preferable for your setup\. If you use both, environment variables will take preference over global settings\.
.
.SH "LOCAL GIT REPOS"
Bundler also allows you to work against a git repository locally instead of using the remote version\. This can be achieved by setting up a local override:
.
.IP "" 4
.
.nf
bundle config set \-\-local local\.GEM_NAME /path/to/local/git/repository
.
.fi
.
.IP "" 0
.
.P
For example, in order to use a local Rack repository, a developer could call:
.
.IP "" 4
.
.nf
bundle config set \-\-local local\.rack ~/Work/git/rack
.
.fi
.
.IP "" 0
.
.P
Now instead of checking out the remote git repository, the local override will be used\. Similar to a path source, every time the local git repository change, changes will be automatically picked up by Bundler\. This means a commit in the local git repo will update the revision in the \fBGemfile\.lock\fR to the local git repo revision\. This requires the same attention as git submodules\. Before pushing to the remote, you need to ensure the local override was pushed, otherwise you may point to a commit that only exists in your local machine\. You\'ll also need to CGI escape your usernames and passwords as well\.
.
.P
Bundler does many checks to ensure a developer won\'t work with invalid references\. Particularly, we force a developer to specify a branch in the \fBGemfile\fR in order to use this feature\. If the branch specified in the \fBGemfile\fR and the current branch in the local git repository do not match, Bundler will abort\. This ensures that a developer is always working against the correct branches, and prevents accidental locking to a different branch\.
.
.P
Finally, Bundler also ensures that the current revision in the \fBGemfile\.lock\fR exists in the local git repository\. By doing this, Bundler forces you to fetch the latest changes in the remotes\.
.
.SH "MIRRORS OF GEM SOURCES"
Bundler supports overriding gem sources with mirrors\. This allows you to configure rubygems\.org as the gem source in your Gemfile while still using your mirror to fetch gems\.
.
.IP "" 4
.
.nf
bundle config set \-\-global mirror\.SOURCE_URL MIRROR_URL
.
.fi
.
.IP "" 0
.
.P
For example, to use a mirror of https://rubygems\.org hosted at https://example\.org:
.
.IP "" 4
.
.nf
bundle config set \-\-global mirror\.https://rubygems\.org https://example\.org
.
.fi
.
.IP "" 0
.
.P
Each mirror also provides a fallback timeout setting\. If the mirror does not respond within the fallback timeout, Bundler will try to use the original server instead of the mirror\.
.
.IP "" 4
.
.nf
bundle config set \-\-global mirror\.SOURCE_URL\.fallback_timeout TIMEOUT
.
.fi
.
.IP "" 0
.
.P
For example, to fall back to rubygems\.org after 3 seconds:
.
.IP "" 4
.
.nf
bundle config set \-\-global mirror\.https://rubygems\.org\.fallback_timeout 3
.
.fi
.
.IP "" 0
.
.P
The default fallback timeout is 0\.1 seconds, but the setting can currently only accept whole seconds (for example, 1, 15, or 30)\.
.
.SH "CREDENTIALS FOR GEM SOURCES"
Bundler allows you to configure credentials for any gem source, which allows you to avoid putting secrets into your Gemfile\.
.
.IP "" 4
.
.nf
bundle config set \-\-global SOURCE_HOSTNAME USERNAME:PASSWORD
.
.fi
.
.IP "" 0
.
.P
For example, to save the credentials of user \fBclaudette\fR for the gem source at \fBgems\.longerous\.com\fR, you would run:
.
.IP "" 4
.
.nf
bundle config set \-\-global gems\.longerous\.com claudette:s00pers3krit
.
.fi
.
.IP "" 0
.
.P
Or you can set the credentials as an environment variable like this:
.
.IP "" 4
.
.nf
export BUNDLE_GEMS__LONGEROUS__COM="claudette:s00pers3krit"
.
.fi
.
.IP "" 0
.
.P
For gems with a git source with HTTP(S) URL you can specify credentials like so:
.
.IP "" 4
.
.nf
bundle config set \-\-global https://github\.com/rubygems/rubygems\.git username:password
.
.fi
.
.IP "" 0
.
.P
Or you can set the credentials as an environment variable like so:
.
.IP "" 4
.
.nf
export BUNDLE_GITHUB__COM=username:password
.
.fi
.
.IP "" 0
.
.P
This is especially useful for private repositories on hosts such as GitHub, where you can use personal OAuth tokens:
.
.IP "" 4
.
.nf
export BUNDLE_GITHUB__COM=abcd0123generatedtoken:x\-oauth\-basic
.
.fi
.
.IP "" 0
.
.P
Note that any configured credentials will be redacted by informative commands such as \fBbundle config list\fR or \fBbundle config get\fR, unless you use the \fB\-\-parseable\fR flag\. This is to avoid unintentionally leaking credentials when copy\-pasting bundler output\.
.
.P
Also note that to guarantee a sane mapping between valid environment variable names and valid host names, bundler makes the following transformations:
.
.IP "\(bu" 4
Any \fB\-\fR characters in a host name are mapped to a triple dash (\fB___\fR) in the corresponding environment variable\.
.
.IP "\(bu" 4
Any \fB\.\fR characters in a host name are mapped to a double dash (\fB__\fR) in the corresponding environment variable\.
.
.IP "" 0
.
.P
This means that if you have a gem server named \fBmy\.gem\-host\.com\fR, you\'ll need to use the \fBBUNDLE_MY__GEM___HOST__COM\fR variable to configure credentials for it through ENV\.
.
.SH "CONFIGURE BUNDLER DIRECTORIES"
Bundler\'s home, config, cache and plugin directories are able to be configured through environment variables\. The default location for Bundler\'s home directory is \fB~/\.bundle\fR, which all directories inherit from by default\. The following outlines the available environment variables and their default values
.
.IP "" 4
.
.nf
BUNDLE_USER_HOME : $HOME/\.bundle
BUNDLE_USER_CACHE : $BUNDLE_USER_HOME/cache
BUNDLE_USER_CONFIG : $BUNDLE_USER_HOME/config
BUNDLE_USER_PLUGIN : $BUNDLE_USER_HOME/plugin
.
.fi
.
.IP "" 0

View File

@@ -1,405 +0,0 @@
bundle-config(1) -- Set bundler configuration options
=====================================================
## SYNOPSIS
`bundle config` list<br>
`bundle config` [get] NAME<br>
`bundle config` [set] NAME VALUE<br>
`bundle config` unset NAME
## DESCRIPTION
This command allows you to interact with Bundler's configuration system.
Bundler loads configuration settings in this order:
1. Local config (`<project_root>/.bundle/config` or `$BUNDLE_APP_CONFIG/config`)
2. Environmental variables (`ENV`)
3. Global config (`~/.bundle/config`)
4. Bundler default config
Executing `bundle config list` will print a list of all bundler
configuration for the current bundle, and where that configuration
was set.
Executing `bundle config get <name>` will print the value of that configuration
setting, and where it was set.
Executing `bundle config set <name> <value>` defaults to setting `local`
configuration if executing from within a local application, otherwise it will
set `global` configuration. See `--local` and `--global` options below.
Executing `bundle config set --local <name> <value>` will set that configuration
in the directory for the local application. The configuration will be stored in
`<project_root>/.bundle/config`. If `BUNDLE_APP_CONFIG` is set, the configuration
will be stored in `$BUNDLE_APP_CONFIG/config`.
Executing `bundle config set --global <name> <value>` will set that
configuration to the value specified for all bundles executed as the current
user. The configuration will be stored in `~/.bundle/config`. If <name> already
is set, <name> will be overridden and user will be warned.
Executing `bundle config unset <name>` will delete the configuration in both
local and global sources.
Executing `bundle config unset --global <name>` will delete the configuration
only from the user configuration.
Executing `bundle config unset --local <name>` will delete the configuration
only from the local application.
Executing bundle with the `BUNDLE_IGNORE_CONFIG` environment variable set will
cause it to ignore all configuration.
## REMEMBERING OPTIONS
Flags passed to `bundle install` or the Bundler runtime, such as `--path foo` or
`--without production`, are remembered between commands and saved to your local
application's configuration (normally, `./.bundle/config`).
However, this will be changed in bundler 3, so it's better not to rely on this
behavior. If these options must be remembered, it's better to set them using
`bundle config` (e.g., `bundle config set --local path foo`).
The options that can be configured are:
* `bin`:
Creates a directory (defaults to `~/bin`) and place any executables from the
gem there. These executables run in Bundler's context. If used, you might add
this directory to your environment's `PATH` variable. For instance, if the
`rails` gem comes with a `rails` executable, this flag will create a
`bin/rails` executable that ensures that all referred dependencies will be
resolved using the bundled gems.
* `deployment`:
In deployment mode, Bundler will 'roll-out' the bundle for
`production` use. Please check carefully if you want to have this option
enabled in `development` or `test` environments.
* `only`:
A space-separated list of groups to install only gems of the specified groups.
* `path`:
The location to install the specified gems to. This defaults to Rubygems'
setting. Bundler shares this location with Rubygems, `gem install ...` will
have gem installed there, too. Therefore, gems installed without a
`--path ...` setting will show up by calling `gem list`. Accordingly, gems
installed to other locations will not get listed.
* `without`:
A space-separated list of groups referencing gems to skip during installation.
* `with`:
A space-separated list of **optional** groups referencing gems to include during installation.
## BUILD OPTIONS
You can use `bundle config` to give Bundler the flags to pass to the gem
installer every time bundler tries to install a particular gem.
A very common example, the `mysql` gem, requires Snow Leopard users to
pass configuration flags to `gem install` to specify where to find the
`mysql_config` executable.
gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
Since the specific location of that executable can change from machine
to machine, you can specify these flags on a per-machine basis.
bundle config set --global build.mysql --with-mysql-config=/usr/local/mysql/bin/mysql_config
After running this command, every time bundler needs to install the
`mysql` gem, it will pass along the flags you specified.
## CONFIGURATION KEYS
Configuration keys in bundler have two forms: the canonical form and the
environment variable form.
For instance, passing the `--without` flag to [bundle install(1)](bundle-install.1.html)
prevents Bundler from installing certain groups specified in the Gemfile(5). Bundler
persists this value in `app/.bundle/config` so that calls to `Bundler.setup`
do not try to find gems from the `Gemfile` that you didn't install. Additionally,
subsequent calls to [bundle install(1)](bundle-install.1.html) remember this setting
and skip those groups.
The canonical form of this configuration is `"without"`. To convert the canonical
form to the environment variable form, capitalize it, and prepend `BUNDLE_`. The
environment variable form of `"without"` is `BUNDLE_WITHOUT`.
Any periods in the configuration keys must be replaced with two underscores when
setting it via environment variables. The configuration key `local.rack` becomes
the environment variable `BUNDLE_LOCAL__RACK`.
## LIST OF AVAILABLE KEYS
The following is a list of all configuration keys and their purpose. You can
learn more about their operation in [bundle install(1)](bundle-install.1.html).
* `allow_deployment_source_credential_changes` (`BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES`):
When in deployment mode, allow changing the credentials to a gem's source.
Ex: `https://some.host.com/gems/path/` -> `https://user_name:password@some.host.com/gems/path`
* `allow_offline_install` (`BUNDLE_ALLOW_OFFLINE_INSTALL`):
Allow Bundler to use cached data when installing without network access.
* `auto_clean_without_path` (`BUNDLE_AUTO_CLEAN_WITHOUT_PATH`):
Automatically run `bundle clean` after installing when an explicit `path`
has not been set and Bundler is not installing into the system gems.
* `auto_install` (`BUNDLE_AUTO_INSTALL`):
Automatically run `bundle install` when gems are missing.
* `bin` (`BUNDLE_BIN`):
Install executables from gems in the bundle to the specified directory.
Defaults to `false`.
* `cache_all` (`BUNDLE_CACHE_ALL`):
Cache all gems, including path and git gems. This needs to be explicitly
configured on bundler 1 and bundler 2, but will be the default on bundler 3.
* `cache_all_platforms` (`BUNDLE_CACHE_ALL_PLATFORMS`):
Cache gems for all platforms.
* `cache_path` (`BUNDLE_CACHE_PATH`):
The directory that bundler will place cached gems in when running
<code>bundle package</code>, and that bundler will look in when installing gems.
Defaults to `vendor/cache`.
* `clean` (`BUNDLE_CLEAN`):
Whether Bundler should run `bundle clean` automatically after
`bundle install`.
* `console` (`BUNDLE_CONSOLE`):
The console that `bundle console` starts. Defaults to `irb`.
* `default_install_uses_path` (`BUNDLE_DEFAULT_INSTALL_USES_PATH`):
Whether a `bundle install` without an explicit `--path` argument defaults
to installing gems in `.bundle`.
* `deployment` (`BUNDLE_DEPLOYMENT`):
Disallow changes to the `Gemfile`. When the `Gemfile` is changed and the
lockfile has not been updated, running Bundler commands will be blocked.
* `disable_checksum_validation` (`BUNDLE_DISABLE_CHECKSUM_VALIDATION`):
Allow installing gems even if they do not match the checksum provided by
RubyGems.
* `disable_exec_load` (`BUNDLE_DISABLE_EXEC_LOAD`):
Stop Bundler from using `load` to launch an executable in-process in
`bundle exec`.
* `disable_local_branch_check` (`BUNDLE_DISABLE_LOCAL_BRANCH_CHECK`):
Allow Bundler to use a local git override without a branch specified in the
Gemfile.
* `disable_local_revision_check` (`BUNDLE_DISABLE_LOCAL_REVISION_CHECK`):
Allow Bundler to use a local git override without checking if the revision
present in the lockfile is present in the repository.
* `disable_shared_gems` (`BUNDLE_DISABLE_SHARED_GEMS`):
Stop Bundler from accessing gems installed to RubyGems' normal location.
* `disable_version_check` (`BUNDLE_DISABLE_VERSION_CHECK`):
Stop Bundler from checking if a newer Bundler version is available on
rubygems.org.
* `force_ruby_platform` (`BUNDLE_FORCE_RUBY_PLATFORM`):
Ignore the current machine's platform and install only `ruby` platform gems.
As a result, gems with native extensions will be compiled from source.
* `frozen` (`BUNDLE_FROZEN`):
Disallow changes to the `Gemfile`. When the `Gemfile` is changed and the
lockfile has not been updated, running Bundler commands will be blocked.
Defaults to `true` when `--deployment` is used.
* `gem.github_username` (`BUNDLE_GEM__GITHUB_USERNAME`):
Sets a GitHub username or organization to be used in `README` file when you
create a new gem via `bundle gem` command. It can be overridden by passing an
explicit `--github-username` flag to `bundle gem`.
* `gem.push_key` (`BUNDLE_GEM__PUSH_KEY`):
Sets the `--key` parameter for `gem push` when using the `rake release`
command with a private gemstash server.
* `gemfile` (`BUNDLE_GEMFILE`):
The name of the file that bundler should use as the `Gemfile`. This location
of this file also sets the root of the project, which is used to resolve
relative paths in the `Gemfile`, among other things. By default, bundler
will search up from the current working directory until it finds a
`Gemfile`.
* `global_gem_cache` (`BUNDLE_GLOBAL_GEM_CACHE`):
Whether Bundler should cache all gems globally, rather than locally to the
installing Ruby installation.
* `ignore_funding_requests` (`BUNDLE_IGNORE_FUNDING_REQUESTS`):
When set, no funding requests will be printed.
* `ignore_messages` (`BUNDLE_IGNORE_MESSAGES`):
When set, no post install messages will be printed. To silence a single gem,
use dot notation like `ignore_messages.httparty true`.
* `init_gems_rb` (`BUNDLE_INIT_GEMS_RB`):
Generate a `gems.rb` instead of a `Gemfile` when running `bundle init`.
* `jobs` (`BUNDLE_JOBS`):
The number of gems Bundler can install in parallel. Defaults to the number of
available processors.
* `no_install` (`BUNDLE_NO_INSTALL`):
Whether `bundle package` should skip installing gems.
* `no_prune` (`BUNDLE_NO_PRUNE`):
Whether Bundler should leave outdated gems unpruned when caching.
* `only` (`BUNDLE_ONLY`):
A space-separated list of groups to install only gems of the specified groups.
* `path` (`BUNDLE_PATH`):
The location on disk where all gems in your bundle will be located regardless
of `$GEM_HOME` or `$GEM_PATH` values. Bundle gems not found in this location
will be installed by `bundle install`. Defaults to `Gem.dir`. When --deployment
is used, defaults to vendor/bundle.
* `path.system` (`BUNDLE_PATH__SYSTEM`):
Whether Bundler will install gems into the default system path (`Gem.dir`).
* `path_relative_to_cwd` (`BUNDLE_PATH_RELATIVE_TO_CWD`)
Makes `--path` relative to the CWD instead of the `Gemfile`.
* `plugins` (`BUNDLE_PLUGINS`):
Enable Bundler's experimental plugin system.
* `prefer_patch` (BUNDLE_PREFER_PATCH):
Prefer updating only to next patch version during updates. Makes `bundle update` calls equivalent to `bundler update --patch`.
* `print_only_version_number` (`BUNDLE_PRINT_ONLY_VERSION_NUMBER`):
Print only version number from `bundler --version`.
* `redirect` (`BUNDLE_REDIRECT`):
The number of redirects allowed for network requests. Defaults to `5`.
* `retry` (`BUNDLE_RETRY`):
The number of times to retry failed network requests. Defaults to `3`.
* `setup_makes_kernel_gem_public` (`BUNDLE_SETUP_MAKES_KERNEL_GEM_PUBLIC`):
Have `Bundler.setup` make the `Kernel#gem` method public, even though
RubyGems declares it as private.
* `shebang` (`BUNDLE_SHEBANG`):
The program name that should be invoked for generated binstubs. Defaults to
the ruby install name used to generate the binstub.
* `silence_deprecations` (`BUNDLE_SILENCE_DEPRECATIONS`):
Whether Bundler should silence deprecation warnings for behavior that will
be changed in the next major version.
* `silence_root_warning` (`BUNDLE_SILENCE_ROOT_WARNING`):
Silence the warning Bundler prints when installing gems as root.
* `ssl_ca_cert` (`BUNDLE_SSL_CA_CERT`):
Path to a designated CA certificate file or folder containing multiple
certificates for trusted CAs in PEM format.
* `ssl_client_cert` (`BUNDLE_SSL_CLIENT_CERT`):
Path to a designated file containing a X.509 client certificate
and key in PEM format.
* `ssl_verify_mode` (`BUNDLE_SSL_VERIFY_MODE`):
The SSL verification mode Bundler uses when making HTTPS requests.
Defaults to verify peer.
* `system_bindir` (`BUNDLE_SYSTEM_BINDIR`):
The location where RubyGems installs binstubs. Defaults to `Gem.bindir`.
* `timeout` (`BUNDLE_TIMEOUT`):
The seconds allowed before timing out for network requests. Defaults to `10`.
* `update_requires_all_flag` (`BUNDLE_UPDATE_REQUIRES_ALL_FLAG`):
Require passing `--all` to `bundle update` when everything should be updated,
and disallow passing no options to `bundle update`.
* `user_agent` (`BUNDLE_USER_AGENT`):
The custom user agent fragment Bundler includes in API requests.
* `with` (`BUNDLE_WITH`):
A `:`-separated list of groups whose gems bundler should install.
* `without` (`BUNDLE_WITHOUT`):
A `:`-separated list of groups whose gems bundler should not install.
In general, you should set these settings per-application by using the applicable
flag to the [bundle install(1)](bundle-install.1.html) or [bundle cache(1)](bundle-cache.1.html) command.
You can set them globally either via environment variables or `bundle config`,
whichever is preferable for your setup. If you use both, environment variables
will take preference over global settings.
## LOCAL GIT REPOS
Bundler also allows you to work against a git repository locally
instead of using the remote version. This can be achieved by setting
up a local override:
bundle config set --local local.GEM_NAME /path/to/local/git/repository
For example, in order to use a local Rack repository, a developer could call:
bundle config set --local local.rack ~/Work/git/rack
Now instead of checking out the remote git repository, the local
override will be used. Similar to a path source, every time the local
git repository change, changes will be automatically picked up by
Bundler. This means a commit in the local git repo will update the
revision in the `Gemfile.lock` to the local git repo revision. This
requires the same attention as git submodules. Before pushing to
the remote, you need to ensure the local override was pushed, otherwise
you may point to a commit that only exists in your local machine.
You'll also need to CGI escape your usernames and passwords as well.
Bundler does many checks to ensure a developer won't work with
invalid references. Particularly, we force a developer to specify
a branch in the `Gemfile` in order to use this feature. If the branch
specified in the `Gemfile` and the current branch in the local git
repository do not match, Bundler will abort. This ensures that
a developer is always working against the correct branches, and prevents
accidental locking to a different branch.
Finally, Bundler also ensures that the current revision in the
`Gemfile.lock` exists in the local git repository. By doing this, Bundler
forces you to fetch the latest changes in the remotes.
## MIRRORS OF GEM SOURCES
Bundler supports overriding gem sources with mirrors. This allows you to
configure rubygems.org as the gem source in your Gemfile while still using your
mirror to fetch gems.
bundle config set --global mirror.SOURCE_URL MIRROR_URL
For example, to use a mirror of https://rubygems.org hosted at https://example.org:
bundle config set --global mirror.https://rubygems.org https://example.org
Each mirror also provides a fallback timeout setting. If the mirror does not
respond within the fallback timeout, Bundler will try to use the original
server instead of the mirror.
bundle config set --global mirror.SOURCE_URL.fallback_timeout TIMEOUT
For example, to fall back to rubygems.org after 3 seconds:
bundle config set --global mirror.https://rubygems.org.fallback_timeout 3
The default fallback timeout is 0.1 seconds, but the setting can currently
only accept whole seconds (for example, 1, 15, or 30).
## CREDENTIALS FOR GEM SOURCES
Bundler allows you to configure credentials for any gem source, which allows
you to avoid putting secrets into your Gemfile.
bundle config set --global SOURCE_HOSTNAME USERNAME:PASSWORD
For example, to save the credentials of user `claudette` for the gem source at
`gems.longerous.com`, you would run:
bundle config set --global gems.longerous.com claudette:s00pers3krit
Or you can set the credentials as an environment variable like this:
export BUNDLE_GEMS__LONGEROUS__COM="claudette:s00pers3krit"
For gems with a git source with HTTP(S) URL you can specify credentials like so:
bundle config set --global https://github.com/rubygems/rubygems.git username:password
Or you can set the credentials as an environment variable like so:
export BUNDLE_GITHUB__COM=username:password
This is especially useful for private repositories on hosts such as GitHub,
where you can use personal OAuth tokens:
export BUNDLE_GITHUB__COM=abcd0123generatedtoken:x-oauth-basic
Note that any configured credentials will be redacted by informative commands
such as `bundle config list` or `bundle config get`, unless you use the
`--parseable` flag. This is to avoid unintentionally leaking credentials when
copy-pasting bundler output.
Also note that to guarantee a sane mapping between valid environment variable
names and valid host names, bundler makes the following transformations:
* Any `-` characters in a host name are mapped to a triple dash (`___`) in the
corresponding environment variable.
* Any `.` characters in a host name are mapped to a double dash (`__`) in the
corresponding environment variable.
This means that if you have a gem server named `my.gem-host.com`, you'll need to
use the `BUNDLE_MY__GEM___HOST__COM` variable to configure credentials for it
through ENV.
## CONFIGURE BUNDLER DIRECTORIES
Bundler's home, config, cache and plugin directories are able to be configured
through environment variables. The default location for Bundler's home directory is
`~/.bundle`, which all directories inherit from by default. The following
outlines the available environment variables and their default values
BUNDLE_USER_HOME : $HOME/.bundle
BUNDLE_USER_CACHE : $BUNDLE_USER_HOME/cache
BUNDLE_USER_CONFIG : $BUNDLE_USER_HOME/config
BUNDLE_USER_PLUGIN : $BUNDLE_USER_HOME/plugin

View File

@@ -1,53 +0,0 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "BUNDLE\-CONSOLE" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-console\fR \- Deprecated way to open an IRB session with the bundle pre\-loaded
.
.SH "SYNOPSIS"
\fBbundle console\fR [GROUP]
.
.SH "DESCRIPTION"
Starts an interactive Ruby console session in the context of the current bundle\.
.
.P
If no \fBGROUP\fR is specified, all gems in the \fBdefault\fR group in the Gemfile(5) \fIhttps://bundler\.io/man/gemfile\.5\.html\fR are preliminarily loaded\.
.
.P
If \fBGROUP\fR is specified, all gems in the given group in the Gemfile in addition to the gems in \fBdefault\fR group are loaded\. Even if the given group does not exist in the Gemfile, IRB console starts without any warning or error\.
.
.P
The environment variable \fBBUNDLE_CONSOLE\fR or \fBbundle config set console\fR can be used to change the shell from the following:
.
.IP "\(bu" 4
\fBirb\fR (default)
.
.IP "\(bu" 4
\fBpry\fR (https://github\.com/pry/pry)
.
.IP "\(bu" 4
\fBripl\fR (https://github\.com/cldwalker/ripl)
.
.IP "" 0
.
.P
\fBbundle console\fR uses irb by default\. An alternative Pry or Ripl can be used with \fBbundle console\fR by adjusting the \fBconsole\fR Bundler setting\. Also make sure that \fBpry\fR or \fBripl\fR is in your Gemfile\.
.
.SH "EXAMPLE"
.
.nf
$ bundle config set console pry
$ bundle console
Resolving dependencies\.\.\.
[1] pry(main)>
.
.fi
.
.SH "NOTES"
This command was deprecated in Bundler 2\.1 and will be removed in 3\.0\. Use \fBbin/console\fR script, which can be generated by \fBbundle gem <NAME>\fR\.
.
.SH "SEE ALSO"
Gemfile(5) \fIhttps://bundler\.io/man/gemfile\.5\.html\fR

View File

@@ -1,44 +0,0 @@
bundle-console(1) -- Deprecated way to open an IRB session with the bundle pre-loaded
=====================================================================================
## SYNOPSIS
`bundle console` [GROUP]
## DESCRIPTION
Starts an interactive Ruby console session in the context of the current bundle.
If no `GROUP` is specified, all gems in the `default` group in the [Gemfile(5)](https://bundler.io/man/gemfile.5.html) are
preliminarily loaded.
If `GROUP` is specified, all gems in the given group in the Gemfile in addition
to the gems in `default` group are loaded. Even if the given group does not
exist in the Gemfile, IRB console starts without any warning or error.
The environment variable `BUNDLE_CONSOLE` or `bundle config set console` can be used to change
the shell from the following:
* `irb` (default)
* `pry` (https://github.com/pry/pry)
* `ripl` (https://github.com/cldwalker/ripl)
`bundle console` uses irb by default. An alternative Pry or Ripl can be used with
`bundle console` by adjusting the `console` Bundler setting. Also make sure that
`pry` or `ripl` is in your Gemfile.
## EXAMPLE
$ bundle config set console pry
$ bundle console
Resolving dependencies...
[1] pry(main)>
## NOTES
This command was deprecated in Bundler 2.1 and will be removed in 3.0.
Use `bin/console` script, which can be generated by `bundle gem <NAME>`.
## SEE ALSO
[Gemfile(5)](https://bundler.io/man/gemfile.5.html)

View File

@@ -1,44 +0,0 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "BUNDLE\-DOCTOR" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-doctor\fR \- Checks the bundle for common problems
.
.SH "SYNOPSIS"
\fBbundle doctor\fR [\-\-quiet] [\-\-gemfile=GEMFILE]
.
.SH "DESCRIPTION"
Checks your Gemfile and gem environment for common problems\. If issues are detected, Bundler prints them and exits status 1\. Otherwise, Bundler prints a success message and exits status 0\.
.
.P
Examples of common problems caught by bundle\-doctor include:
.
.IP "\(bu" 4
Invalid Bundler settings
.
.IP "\(bu" 4
Mismatched Ruby versions
.
.IP "\(bu" 4
Mismatched platforms
.
.IP "\(bu" 4
Uninstalled gems
.
.IP "\(bu" 4
Missing dependencies
.
.IP "" 0
.
.SH "OPTIONS"
.
.TP
\fB\-\-quiet\fR
Only output warnings and errors\.
.
.TP
\fB\-\-gemfile=<gemfile>\fR
The location of the Gemfile(5) which Bundler should use\. This defaults to a Gemfile(5) in the current working directory\. In general, Bundler will assume that the location of the Gemfile(5) is also the project\'s root and will try to find \fBGemfile\.lock\fR and \fBvendor/cache\fR relative to this location\.

View File

@@ -1,33 +0,0 @@
bundle-doctor(1) -- Checks the bundle for common problems
=========================================================
## SYNOPSIS
`bundle doctor` [--quiet]
[--gemfile=GEMFILE]
## DESCRIPTION
Checks your Gemfile and gem environment for common problems. If issues
are detected, Bundler prints them and exits status 1. Otherwise,
Bundler prints a success message and exits status 0.
Examples of common problems caught by bundle-doctor include:
* Invalid Bundler settings
* Mismatched Ruby versions
* Mismatched platforms
* Uninstalled gems
* Missing dependencies
## OPTIONS
* `--quiet`:
Only output warnings and errors.
* `--gemfile=<gemfile>`:
The location of the Gemfile(5) which Bundler should use. This defaults
to a Gemfile(5) in the current working directory. In general, Bundler
will assume that the location of the Gemfile(5) is also the project's
root and will try to find `Gemfile.lock` and `vendor/cache` relative
to this location.

View File

@@ -1,165 +0,0 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "BUNDLE\-EXEC" "1" "August 2023" "" ""
.
.SH "NAME"
\fBbundle\-exec\fR \- Execute a command in the context of the bundle
.
.SH "SYNOPSIS"
\fBbundle exec\fR [\-\-keep\-file\-descriptors] \fIcommand\fR
.
.SH "DESCRIPTION"
This command executes the command, making all gems specified in the [\fBGemfile(5)\fR][Gemfile(5)] available to \fBrequire\fR in Ruby programs\.
.
.P
Essentially, if you would normally have run something like \fBrspec spec/my_spec\.rb\fR, and you want to use the gems specified in the [\fBGemfile(5)\fR][Gemfile(5)] and installed via bundle install(1) \fIbundle\-install\.1\.html\fR, you should run \fBbundle exec rspec spec/my_spec\.rb\fR\.
.
.P
Note that \fBbundle exec\fR does not require that an executable is available on your shell\'s \fB$PATH\fR\.
.
.SH "OPTIONS"
.
.TP
\fB\-\-keep\-file\-descriptors\fR
Exec in Ruby 2\.0 began discarding non\-standard file descriptors\. When this flag is passed, exec will revert to the 1\.9 behaviour of passing all file descriptors to the new process\.
.
.SH "BUNDLE INSTALL \-\-BINSTUBS"
If you use the \fB\-\-binstubs\fR flag in bundle install(1) \fIbundle\-install\.1\.html\fR, Bundler will automatically create a directory (which defaults to \fBapp_root/bin\fR) containing all of the executables available from gems in the bundle\.
.
.P
After using \fB\-\-binstubs\fR, \fBbin/rspec spec/my_spec\.rb\fR is identical to \fBbundle exec rspec spec/my_spec\.rb\fR\.
.
.SH "ENVIRONMENT MODIFICATIONS"
\fBbundle exec\fR makes a number of changes to the shell environment, then executes the command you specify in full\.
.
.IP "\(bu" 4
make sure that it\'s still possible to shell out to \fBbundle\fR from inside a command invoked by \fBbundle exec\fR (using \fB$BUNDLE_BIN_PATH\fR)
.
.IP "\(bu" 4
put the directory containing executables (like \fBrails\fR, \fBrspec\fR, \fBrackup\fR) for your bundle on \fB$PATH\fR
.
.IP "\(bu" 4
make sure that if bundler is invoked in the subshell, it uses the same \fBGemfile\fR (by setting \fBBUNDLE_GEMFILE\fR)
.
.IP "\(bu" 4
add \fB\-rbundler/setup\fR to \fB$RUBYOPT\fR, which makes sure that Ruby programs invoked in the subshell can see the gems in the bundle
.
.IP "" 0
.
.P
It also modifies Rubygems:
.
.IP "\(bu" 4
disallow loading additional gems not in the bundle
.
.IP "\(bu" 4
modify the \fBgem\fR method to be a no\-op if a gem matching the requirements is in the bundle, and to raise a \fBGem::LoadError\fR if it\'s not
.
.IP "\(bu" 4
Define \fBGem\.refresh\fR to be a no\-op, since the source index is always frozen when using bundler, and to prevent gems from the system leaking into the environment
.
.IP "\(bu" 4
Override \fBGem\.bin_path\fR to use the gems in the bundle, making system executables work
.
.IP "\(bu" 4
Add all gems in the bundle into Gem\.loaded_specs
.
.IP "" 0
.
.P
Finally, \fBbundle exec\fR also implicitly modifies \fBGemfile\.lock\fR if the lockfile and the Gemfile do not match\. Bundler needs the Gemfile to determine things such as a gem\'s groups, \fBautorequire\fR, and platforms, etc\., and that information isn\'t stored in the lockfile\. The Gemfile and lockfile must be synced in order to \fBbundle exec\fR successfully, so \fBbundle exec\fR updates the lockfile beforehand\.
.
.SS "Loading"
By default, when attempting to \fBbundle exec\fR to a file with a ruby shebang, Bundler will \fBKernel\.load\fR that file instead of using \fBKernel\.exec\fR\. For the vast majority of cases, this is a performance improvement\. In a rare few cases, this could cause some subtle side\-effects (such as dependence on the exact contents of \fB$0\fR or \fB__FILE__\fR) and the optimization can be disabled by enabling the \fBdisable_exec_load\fR setting\.
.
.SS "Shelling out"
Any Ruby code that opens a subshell (like \fBsystem\fR, backticks, or \fB%x{}\fR) will automatically use the current Bundler environment\. If you need to shell out to a Ruby command that is not part of your current bundle, use the \fBwith_unbundled_env\fR method with a block\. Any subshells created inside the block will be given the environment present before Bundler was activated\. For example, Homebrew commands run Ruby, but don\'t work inside a bundle:
.
.IP "" 4
.
.nf
Bundler\.with_unbundled_env do
`brew install wget`
end
.
.fi
.
.IP "" 0
.
.P
Using \fBwith_unbundled_env\fR is also necessary if you are shelling out to a different bundle\. Any Bundler commands run in a subshell will inherit the current Gemfile, so commands that need to run in the context of a different bundle also need to use \fBwith_unbundled_env\fR\.
.
.IP "" 4
.
.nf
Bundler\.with_unbundled_env do
Dir\.chdir "/other/bundler/project" do
`bundle exec \./script`
end
end
.
.fi
.
.IP "" 0
.
.P
Bundler provides convenience helpers that wrap \fBsystem\fR and \fBexec\fR, and they can be used like this:
.
.IP "" 4
.
.nf
Bundler\.clean_system(\'brew install wget\')
Bundler\.clean_exec(\'brew install wget\')
.
.fi
.
.IP "" 0
.
.SH "RUBYGEMS PLUGINS"
At present, the Rubygems plugin system requires all files named \fBrubygems_plugin\.rb\fR on the load path of \fIany\fR installed gem when any Ruby code requires \fBrubygems\.rb\fR\. This includes executables installed into the system, like \fBrails\fR, \fBrackup\fR, and \fBrspec\fR\.
.
.P
Since Rubygems plugins can contain arbitrary Ruby code, they commonly end up activating themselves or their dependencies\.
.
.P
For instance, the \fBgemcutter 0\.5\fR gem depended on \fBjson_pure\fR\. If you had that version of gemcutter installed (even if you \fIalso\fR had a newer version without this problem), Rubygems would activate \fBgemcutter 0\.5\fR and \fBjson_pure <latest>\fR\.
.
.P
If your Gemfile(5) also contained \fBjson_pure\fR (or a gem with a dependency on \fBjson_pure\fR), the latest version on your system might conflict with the version in your Gemfile(5), or the snapshot version in your \fBGemfile\.lock\fR\.
.
.P
If this happens, bundler will say:
.
.IP "" 4
.
.nf
You have already activated json_pure 1\.4\.6 but your Gemfile
requires json_pure 1\.4\.3\. Consider using bundle exec\.
.
.fi
.
.IP "" 0
.
.P
In this situation, you almost certainly want to remove the underlying gem with the problematic gem plugin\. In general, the authors of these plugins (in this case, the \fBgemcutter\fR gem) have released newer versions that are more careful in their plugins\.
.
.P
You can find a list of all the gems containing gem plugins by running
.
.IP "" 4
.
.nf
ruby \-e "puts Gem\.find_files(\'rubygems_plugin\.rb\')"
.
.fi
.
.IP "" 0
.
.P
At the very least, you should remove all but the newest version of each gem plugin, and also remove all gem plugins that you aren\'t using (\fBgem uninstall gem_name\fR)\.

Some files were not shown because too many files have changed in this diff Show More