rubygems-update 3.4.7 → 3.4.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +57 -0
- data/Manifest.txt +4 -0
- data/POLICIES.md +5 -5
- data/README.md +17 -4
- data/bundler/CHANGELOG.md +48 -0
- data/bundler/README.md +1 -4
- data/bundler/lib/bundler/build_metadata.rb +2 -2
- data/bundler/lib/bundler/endpoint_specification.rb +0 -4
- data/bundler/lib/bundler/environment_preserver.rb +2 -2
- data/bundler/lib/bundler/fetcher/dependency.rb +1 -5
- data/bundler/lib/bundler/fetcher.rb +2 -2
- data/bundler/lib/bundler/incomplete_specification.rb +24 -0
- data/bundler/lib/bundler/index.rb +2 -2
- data/bundler/lib/bundler/injector.rb +1 -1
- data/bundler/lib/bundler/installer/parallel_installer.rb +1 -14
- data/bundler/lib/bundler/lazy_specification.rb +4 -8
- data/bundler/lib/bundler/lockfile_generator.rb +1 -1
- data/bundler/lib/bundler/lockfile_parser.rb +11 -11
- data/bundler/lib/bundler/plugin.rb +1 -1
- data/bundler/lib/bundler/remote_specification.rb +2 -6
- data/bundler/lib/bundler/resolver/base.rb +5 -3
- data/bundler/lib/bundler/resolver.rb +6 -9
- data/bundler/lib/bundler/rubygems_integration.rb +1 -1
- data/bundler/lib/bundler/settings.rb +1 -1
- data/bundler/lib/bundler/setup.rb +4 -1
- data/bundler/lib/bundler/shared_helpers.rb +1 -1
- data/bundler/lib/bundler/source/git/git_proxy.rb +21 -4
- data/bundler/lib/bundler/source/git.rb +2 -1
- data/bundler/lib/bundler/source/path.rb +1 -1
- data/bundler/lib/bundler/source/rubygems.rb +1 -2
- data/bundler/lib/bundler/spec_set.rb +19 -12
- data/bundler/lib/bundler/templates/Executable.bundler +1 -1
- data/bundler/lib/bundler/templates/newgem/Gemfile.tt +1 -1
- data/bundler/lib/bundler/templates/newgem/Rakefile.tt +10 -0
- data/bundler/lib/bundler/templates/newgem/github/workflows/main.yml.tt +1 -1
- data/bundler/lib/bundler/templates/newgem/newgem.gemspec.tt +1 -1
- data/bundler/lib/bundler/uri_normalizer.rb +23 -0
- data/bundler/lib/bundler/vendor/pub_grub/lib/pub_grub/incompatibility.rb +0 -5
- data/bundler/lib/bundler/version.rb +1 -1
- data/bundler/lib/bundler.rb +11 -9
- data/lib/rubygems/bundler_version_finder.rb +1 -1
- data/lib/rubygems/command.rb +10 -6
- data/lib/rubygems/command_manager.rb +1 -0
- data/lib/rubygems/commands/exec_command.rb +248 -0
- data/lib/rubygems/commands/help_command.rb +3 -3
- data/lib/rubygems/commands/pristine_command.rb +9 -0
- data/lib/rubygems/commands/uninstall_command.rb +3 -0
- data/lib/rubygems/core_ext/kernel_require.rb +0 -10
- data/lib/rubygems/defaults.rb +2 -2
- data/lib/rubygems/dependency.rb +1 -1
- data/lib/rubygems/ext/builder.rb +17 -0
- data/lib/rubygems/ext/ext_conf_builder.rb +1 -2
- data/lib/rubygems/ext/rake_builder.rb +1 -1
- data/lib/rubygems/package/tar_header.rb +1 -1
- data/lib/rubygems/package/tar_reader/entry.rb +82 -3
- data/lib/rubygems/package/tar_reader.rb +0 -28
- data/lib/rubygems/platform.rb +2 -2
- data/lib/rubygems/request_set/gem_dependency_api.rb +0 -1
- data/lib/rubygems/requirement.rb +1 -1
- data/lib/rubygems/resolver/stats.rb +1 -1
- data/lib/rubygems/source/git.rb +1 -1
- data/lib/rubygems/specification.rb +9 -1
- data/lib/rubygems/stub_specification.rb +7 -7
- data/lib/rubygems/text.rb +1 -1
- data/lib/rubygems/util/licenses.rb +2 -2
- data/lib/rubygems/version.rb +2 -2
- data/lib/rubygems.rb +3 -3
- data/rubygems-update.gemspec +1 -1
- data/test/rubygems/helper.rb +2 -3
- data/test/rubygems/package/tar_test_case.rb +50 -15
- data/test/rubygems/simple_gem.rb +1 -1
- data/test/rubygems/test_gem.rb +29 -0
- data/test/rubygems/test_gem_bundler_version_finder.rb +2 -2
- data/test/rubygems/test_gem_commands_exec_command.rb +851 -0
- data/test/rubygems/test_gem_commands_install_command.rb +1 -1
- data/test/rubygems/test_gem_commands_pristine_command.rb +48 -0
- data/test/rubygems/test_gem_commands_setup_command.rb +1 -1
- data/test/rubygems/test_gem_commands_uninstall_command.rb +31 -14
- data/test/rubygems/test_gem_ext_builder.rb +3 -5
- data/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock +20 -6
- data/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml +1 -1
- data/test/rubygems/test_gem_gem_runner.rb +6 -1
- data/test/rubygems/test_gem_package.rb +0 -25
- data/test/rubygems/test_gem_package_tar_reader.rb +48 -1
- data/test/rubygems/test_gem_package_tar_reader_entry.rb +102 -15
- data/test/rubygems/test_gem_remote_fetcher.rb +1 -1
- data/test/rubygems/test_gem_request_set_gem_dependency_api.rb +10 -10
- data/test/rubygems/test_gem_request_set_lockfile_parser.rb +2 -2
- data/test/rubygems/test_gem_resolver_git_set.rb +9 -9
- data/test/rubygems/test_gem_resolver_git_specification.rb +1 -1
- data/test/rubygems/test_gem_source.rb +1 -1
- data/test/rubygems/test_gem_source_git.rb +13 -12
- data/test/rubygems/test_gem_source_installed.rb +1 -1
- data/test/rubygems/test_gem_source_lock.rb +3 -3
- data/test/rubygems/test_gem_source_vendor.rb +1 -1
- data/test/rubygems/test_gem_specification.rb +19 -2
- data/test/rubygems/test_gem_version.rb +2 -2
- data/test/rubygems/test_kernel.rb +0 -8
- metadata +7 -3
@@ -28,8 +28,9 @@ module Bundler
|
|
28
28
|
def initialize(command, path, extra_info = nil)
|
29
29
|
@command = command
|
30
30
|
|
31
|
-
msg = String.new
|
32
|
-
msg << "
|
31
|
+
msg = String.new("Git error: command `#{command}`")
|
32
|
+
msg << " in directory #{path}" if path
|
33
|
+
msg << " has failed."
|
33
34
|
msg << "\n#{extra_info}" if extra_info
|
34
35
|
super msg
|
35
36
|
end
|
@@ -153,9 +154,20 @@ module Bundler
|
|
153
154
|
SharedHelpers.filesystem_access(path.dirname) do |p|
|
154
155
|
FileUtils.mkdir_p(p)
|
155
156
|
end
|
156
|
-
git_retry "clone", "--bare", "--no-hardlinks", "--quiet", *extra_clone_args, "--", configured_uri, path.to_s
|
157
157
|
|
158
|
-
|
158
|
+
command = ["clone", "--bare", "--no-hardlinks", "--quiet", *extra_clone_args, "--", configured_uri, path.to_s]
|
159
|
+
command_with_no_credentials = check_allowed(command)
|
160
|
+
|
161
|
+
Bundler::Retry.new("`#{command_with_no_credentials}`", [MissingGitRevisionError]).attempts do
|
162
|
+
_, err, status = capture(command, nil)
|
163
|
+
return extra_ref if status.success?
|
164
|
+
|
165
|
+
if err.include?("Could not find remote branch")
|
166
|
+
raise MissingGitRevisionError.new(command_with_no_credentials, nil, explicit_ref, credential_filtered_uri)
|
167
|
+
else
|
168
|
+
raise GitCommandError.new(command_with_no_credentials, path, err)
|
169
|
+
end
|
170
|
+
end
|
159
171
|
end
|
160
172
|
|
161
173
|
def clone_needs_unshallow?
|
@@ -354,6 +366,11 @@ module Bundler
|
|
354
366
|
args += ["--single-branch"]
|
355
367
|
args.unshift("--no-tags") if supports_cloning_with_no_tags?
|
356
368
|
|
369
|
+
# If there's a locked revision, no need to clone any specific branch
|
370
|
+
# or tag, since we will end up checking out that locked revision
|
371
|
+
# anyways.
|
372
|
+
return args if @revision
|
373
|
+
|
357
374
|
args += ["--branch", branch || tag] if branch || tag
|
358
375
|
args
|
359
376
|
end
|
@@ -19,7 +19,7 @@ module Bundler
|
|
19
19
|
# Stringify options that could be set as symbols
|
20
20
|
%w[ref branch tag revision].each {|k| options[k] = options[k].to_s if options[k] }
|
21
21
|
|
22
|
-
@uri = options["uri"] || ""
|
22
|
+
@uri = URINormalizer.normalize_suffix(options["uri"] || "", :trailing_slash => false)
|
23
23
|
@safe_uri = URICredentialsFilter.credential_filtered_uri(@uri)
|
24
24
|
@branch = options["branch"]
|
25
25
|
@ref = options["ref"] || options["branch"] || options["tag"]
|
@@ -173,6 +173,7 @@ module Bundler
|
|
173
173
|
end
|
174
174
|
|
175
175
|
def install(spec, options = {})
|
176
|
+
return if Bundler.settings[:no_install]
|
176
177
|
force = options[:force]
|
177
178
|
|
178
179
|
print_using_message "Using #{version_message(spec, options[:previous_spec])} from #{self}"
|
@@ -337,8 +337,7 @@ module Bundler
|
|
337
337
|
end
|
338
338
|
|
339
339
|
def normalize_uri(uri)
|
340
|
-
uri = uri.to_s
|
341
|
-
uri = "#{uri}/" unless %r{/$}.match?(uri)
|
340
|
+
uri = URINormalizer.normalize_suffix(uri.to_s)
|
342
341
|
require_relative "../vendored_uri"
|
343
342
|
uri = Bundler::URI(uri)
|
344
343
|
raise ArgumentError, "The source must be an absolute URI. For example:\n" \
|
@@ -7,11 +7,8 @@ module Bundler
|
|
7
7
|
include Enumerable
|
8
8
|
include TSort
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
def initialize(specs, incomplete_specs = [])
|
10
|
+
def initialize(specs)
|
13
11
|
@specs = specs
|
14
|
-
@incomplete_specs = incomplete_specs
|
15
12
|
end
|
16
13
|
|
17
14
|
def for(dependencies, check = false, platforms = [nil])
|
@@ -24,6 +21,7 @@ module Bundler
|
|
24
21
|
|
25
22
|
name = dep[0].name
|
26
23
|
platform = dep[1]
|
24
|
+
incomplete = false
|
27
25
|
|
28
26
|
key = [name, platform]
|
29
27
|
next if handled.key?(key)
|
@@ -36,14 +34,19 @@ module Bundler
|
|
36
34
|
|
37
35
|
specs_for_dep.first.dependencies.each do |d|
|
38
36
|
next if d.type == :development
|
37
|
+
incomplete = true if d.name != "bundler" && lookup[d.name].empty?
|
39
38
|
deps << [d, dep[1]]
|
40
39
|
end
|
41
|
-
|
42
|
-
|
40
|
+
else
|
41
|
+
incomplete = true
|
42
|
+
end
|
43
|
+
|
44
|
+
if incomplete && check
|
45
|
+
specs << IncompleteSpecification.new(name, lookup[name])
|
43
46
|
end
|
44
47
|
end
|
45
48
|
|
46
|
-
specs
|
49
|
+
specs.uniq
|
47
50
|
end
|
48
51
|
|
49
52
|
def [](key)
|
@@ -75,10 +78,10 @@ module Bundler
|
|
75
78
|
lookup.dup
|
76
79
|
end
|
77
80
|
|
78
|
-
def materialize(deps)
|
79
|
-
materialized = self.for(deps, true)
|
81
|
+
def materialize(deps, platforms = [nil])
|
82
|
+
materialized = self.for(deps, true, platforms)
|
80
83
|
|
81
|
-
SpecSet.new(materialized
|
84
|
+
SpecSet.new(materialized)
|
82
85
|
end
|
83
86
|
|
84
87
|
# Materialize for all the specs in the spec set, regardless of what platform they're for
|
@@ -95,15 +98,19 @@ module Bundler
|
|
95
98
|
end
|
96
99
|
|
97
100
|
def incomplete_ruby_specs?(deps)
|
98
|
-
|
101
|
+
return false if @specs.empty?
|
99
102
|
|
100
|
-
|
103
|
+
materialize(deps, [Gem::Platform::RUBY]).incomplete_specs.any?
|
101
104
|
end
|
102
105
|
|
103
106
|
def missing_specs
|
104
107
|
@specs.select {|s| s.is_a?(LazySpecification) }
|
105
108
|
end
|
106
109
|
|
110
|
+
def incomplete_specs
|
111
|
+
@specs.select {|s| s.is_a?(IncompleteSpecification) }
|
112
|
+
end
|
113
|
+
|
107
114
|
def merge(set)
|
108
115
|
arr = sorted.dup
|
109
116
|
set.each do |set_spec|
|
@@ -41,6 +41,15 @@ require "standard/rake"
|
|
41
41
|
<% if config[:ext] -%>
|
42
42
|
<% default_task_names.unshift(:compile) -%>
|
43
43
|
<% default_task_names.unshift(:clobber) unless config[:ext] == 'rust' -%>
|
44
|
+
<% if config[:ext] == 'rust' -%>
|
45
|
+
require "rb_sys/extensiontask"
|
46
|
+
|
47
|
+
task build: :compile
|
48
|
+
|
49
|
+
RbSys::ExtensionTask.new(<%= config[:name].inspect %>) do |ext|
|
50
|
+
ext.lib_dir = "lib/<%= config[:namespaced_path] %>"
|
51
|
+
end
|
52
|
+
<% else -%>
|
44
53
|
require "rake/extensiontask"
|
45
54
|
|
46
55
|
task build: :compile
|
@@ -48,6 +57,7 @@ task build: :compile
|
|
48
57
|
Rake::ExtensionTask.new("<%= config[:underscored_name] %>") do |ext|
|
49
58
|
ext.lib_dir = "lib/<%= config[:namespaced_path] %>"
|
50
59
|
end
|
60
|
+
<% end -%>
|
51
61
|
|
52
62
|
<% end -%>
|
53
63
|
<% if default_task_names.size == 1 -%>
|
@@ -20,7 +20,7 @@ jobs:
|
|
20
20
|
- uses: actions/checkout@v3
|
21
21
|
<%- if config[:ext] == 'rust' -%>
|
22
22
|
- name: Set up Ruby & Rust
|
23
|
-
uses: oxidize-rb/actions/setup-ruby-and-rust@
|
23
|
+
uses: oxidize-rb/actions/setup-ruby-and-rust@v1
|
24
24
|
with:
|
25
25
|
ruby-version: ${{ matrix.ruby }}
|
26
26
|
bundler-cache: true
|
@@ -29,7 +29,7 @@ Gem::Specification.new do |spec|
|
|
29
29
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
30
30
|
spec.files = Dir.chdir(__dir__) do
|
31
31
|
`git ls-files -z`.split("\x0").reject do |f|
|
32
|
-
(File.expand_path(f) == __FILE__) || f.
|
32
|
+
(File.expand_path(f) == __FILE__) || f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor])
|
33
33
|
end
|
34
34
|
end
|
35
35
|
spec.bindir = "exe"
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bundler
|
4
|
+
module URINormalizer
|
5
|
+
module_function
|
6
|
+
|
7
|
+
# Normalizes uri to a consistent version, either with or without trailing
|
8
|
+
# slash.
|
9
|
+
#
|
10
|
+
# TODO: Currently gem sources are locked with a trailing slash, while git
|
11
|
+
# sources are locked without a trailing slash. This should be normalized but
|
12
|
+
# the inconsistency is there for now to avoid changing all lockfiles
|
13
|
+
# including GIT sources. We could normalize this on the next major.
|
14
|
+
#
|
15
|
+
def normalize_suffix(uri, trailing_slash: true)
|
16
|
+
if trailing_slash
|
17
|
+
uri.end_with?("/") ? uri : "#{uri}/"
|
18
|
+
else
|
19
|
+
uri.end_with?("/") ? uri.delete_suffix("/") : uri
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -8,9 +8,6 @@ module Bundler::PubGrub
|
|
8
8
|
InvalidDependency = Struct.new(:package, :constraint) do
|
9
9
|
end
|
10
10
|
|
11
|
-
CircularDependency = Struct.new(:package, :constraint) do
|
12
|
-
end
|
13
|
-
|
14
11
|
NoVersions = Struct.new(:constraint) do
|
15
12
|
end
|
16
13
|
|
@@ -66,8 +63,6 @@ module Bundler::PubGrub
|
|
66
63
|
"#{terms[0].to_s(allow_every: true)} depends on #{terms[1].invert}"
|
67
64
|
when Bundler::PubGrub::Incompatibility::InvalidDependency
|
68
65
|
"#{terms[0].to_s(allow_every: true)} depends on unknown package #{cause.package}"
|
69
|
-
when Bundler::PubGrub::Incompatibility::CircularDependency
|
70
|
-
"#{terms[0].to_s(allow_every: true)} depends on itself"
|
71
66
|
when Bundler::PubGrub::Incompatibility::NoVersions
|
72
67
|
"no versions satisfy #{cause.constraint}"
|
73
68
|
when Bundler::PubGrub::Incompatibility::ConflictCause
|
data/bundler/lib/bundler.rb
CHANGED
@@ -39,8 +39,8 @@ module Bundler
|
|
39
39
|
environment_preserver.replace_with_backup
|
40
40
|
SUDO_MUTEX = Thread::Mutex.new
|
41
41
|
|
42
|
-
SAFE_MARSHAL_CLASSES = [Symbol, TrueClass, String, Array, Hash].freeze
|
43
|
-
SAFE_MARSHAL_ERROR = "Unexpected class %s present in marshaled data. Only %s are allowed."
|
42
|
+
SAFE_MARSHAL_CLASSES = [Symbol, TrueClass, String, Array, Hash, Gem::Version, Gem::Specification].freeze
|
43
|
+
SAFE_MARSHAL_ERROR = "Unexpected class %s present in marshaled data. Only %s are allowed."
|
44
44
|
SAFE_MARSHAL_PROC = proc do |object|
|
45
45
|
object.tap do
|
46
46
|
unless SAFE_MARSHAL_CLASSES.include?(object.class)
|
@@ -62,6 +62,7 @@ module Bundler
|
|
62
62
|
autoload :GemHelpers, File.expand_path("bundler/gem_helpers", __dir__)
|
63
63
|
autoload :GemVersionPromoter, File.expand_path("bundler/gem_version_promoter", __dir__)
|
64
64
|
autoload :Graph, File.expand_path("bundler/graph", __dir__)
|
65
|
+
autoload :IncompleteSpecification, File.expand_path("bundler/incomplete_specification", __dir__)
|
65
66
|
autoload :Index, File.expand_path("bundler/index", __dir__)
|
66
67
|
autoload :Injector, File.expand_path("bundler/injector", __dir__)
|
67
68
|
autoload :Installer, File.expand_path("bundler/installer", __dir__)
|
@@ -85,6 +86,7 @@ module Bundler
|
|
85
86
|
autoload :StubSpecification, File.expand_path("bundler/stub_specification", __dir__)
|
86
87
|
autoload :UI, File.expand_path("bundler/ui", __dir__)
|
87
88
|
autoload :URICredentialsFilter, File.expand_path("bundler/uri_credentials_filter", __dir__)
|
89
|
+
autoload :URINormalizer, File.expand_path("bundler/uri_normalizer", __dir__)
|
88
90
|
|
89
91
|
class << self
|
90
92
|
def configure
|
@@ -506,7 +508,7 @@ EOF
|
|
506
508
|
if File.file?(executable) && File.executable?(executable)
|
507
509
|
executable
|
508
510
|
elsif paths = ENV["PATH"]
|
509
|
-
quote = '"'
|
511
|
+
quote = '"'
|
510
512
|
paths.split(File::PATH_SEPARATOR).find do |path|
|
511
513
|
path = path[1..-2] if path.start_with?(quote) && path.end_with?(quote)
|
512
514
|
executable_path = File.expand_path(executable, path)
|
@@ -525,12 +527,6 @@ EOF
|
|
525
527
|
load_marshal(data, :marshal_proc => SAFE_MARSHAL_PROC)
|
526
528
|
end
|
527
529
|
|
528
|
-
def load_marshal(data, marshal_proc: nil)
|
529
|
-
Marshal.load(data, marshal_proc)
|
530
|
-
rescue TypeError => e
|
531
|
-
raise MarshalError, "#{e.class}: #{e.message}"
|
532
|
-
end
|
533
|
-
|
534
530
|
def load_gemspec(file, validate = false)
|
535
531
|
@gemspec_cache ||= {}
|
536
532
|
key = File.expand_path(file)
|
@@ -619,6 +615,12 @@ EOF
|
|
619
615
|
|
620
616
|
private
|
621
617
|
|
618
|
+
def load_marshal(data, marshal_proc: nil)
|
619
|
+
Marshal.load(data, marshal_proc)
|
620
|
+
rescue TypeError => e
|
621
|
+
raise MarshalError, "#{e.class}: #{e.message}"
|
622
|
+
end
|
623
|
+
|
622
624
|
def eval_yaml_gemspec(path, contents)
|
623
625
|
Kernel.require "psych"
|
624
626
|
|
@@ -21,7 +21,7 @@ module Gem::BundlerVersionFinder
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def self.bundle_update_bundler_version
|
24
|
-
return unless File.basename($0) == "bundle"
|
24
|
+
return unless File.basename($0) == "bundle"
|
25
25
|
return unless "update".start_with?(ARGV.first || " ")
|
26
26
|
bundler_version = nil
|
27
27
|
update_index = nil
|
data/lib/rubygems/command.rb
CHANGED
@@ -201,11 +201,15 @@ class Gem::Command
|
|
201
201
|
# respectively.
|
202
202
|
def get_all_gem_names_and_versions
|
203
203
|
get_all_gem_names.map do |name|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
204
|
+
extract_gem_name_and_version(name)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def extract_gem_name_and_version(name) # :nodoc:
|
209
|
+
if /\A(.*):(#{Gem::Requirement::PATTERN_RAW})\z/ =~ name
|
210
|
+
[$1, $2]
|
211
|
+
else
|
212
|
+
[name]
|
209
213
|
end
|
210
214
|
end
|
211
215
|
|
@@ -624,7 +628,7 @@ class Gem::Command
|
|
624
628
|
|
625
629
|
# :stopdoc:
|
626
630
|
|
627
|
-
HELP = <<-HELP
|
631
|
+
HELP = <<-HELP
|
628
632
|
RubyGems is a package manager for Ruby.
|
629
633
|
|
630
634
|
Usage:
|
@@ -0,0 +1,248 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative "../command"
|
3
|
+
require_relative "../dependency_installer"
|
4
|
+
require_relative "../gem_runner"
|
5
|
+
require_relative "../package"
|
6
|
+
require_relative "../version_option"
|
7
|
+
|
8
|
+
class Gem::Commands::ExecCommand < Gem::Command
|
9
|
+
include Gem::VersionOption
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
super "exec", "Run a command from a gem", {
|
13
|
+
version: Gem::Requirement.default,
|
14
|
+
}
|
15
|
+
|
16
|
+
add_version_option
|
17
|
+
add_prerelease_option "to be installed"
|
18
|
+
|
19
|
+
add_option "-g", "--gem GEM", "run the executable from the given gem" do |value, options|
|
20
|
+
options[:gem_name] = value
|
21
|
+
end
|
22
|
+
|
23
|
+
add_option(:"Install/Update", "--conservative",
|
24
|
+
"Prefer the most recent installed version, ",
|
25
|
+
"rather than the latest version overall") do |value, options|
|
26
|
+
options[:conservative] = true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def arguments # :nodoc:
|
31
|
+
"COMMAND the executable command to run"
|
32
|
+
end
|
33
|
+
|
34
|
+
def defaults_str # :nodoc:
|
35
|
+
"--version '#{Gem::Requirement.default}'"
|
36
|
+
end
|
37
|
+
|
38
|
+
def description # :nodoc:
|
39
|
+
<<-EOF
|
40
|
+
The exec command handles installing (if necessary) and running an executable
|
41
|
+
from a gem, regardless of whether that gem is currently installed.
|
42
|
+
|
43
|
+
The exec command can be thought of as a shortcut to running `gem install` and
|
44
|
+
then the executable from the installed gem.
|
45
|
+
|
46
|
+
For example, `gem exec rails new .` will run `rails new .` in the current
|
47
|
+
directory, without having to manually run `gem install rails`.
|
48
|
+
Additionally, the exec command ensures the most recent version of the gem
|
49
|
+
is used (unless run with `--conservative`), and that the gem is not installed
|
50
|
+
to the same gem path as user-installed gems.
|
51
|
+
EOF
|
52
|
+
end
|
53
|
+
|
54
|
+
def usage # :nodoc:
|
55
|
+
"#{program_name} [options --] COMMAND [args]"
|
56
|
+
end
|
57
|
+
|
58
|
+
def execute
|
59
|
+
gem_paths = { "GEM_HOME" => Gem.paths.home, "GEM_PATH" => Gem.paths.path.join(File::PATH_SEPARATOR), "GEM_SPEC_CACHE" => Gem.paths.spec_cache_dir }.compact
|
60
|
+
|
61
|
+
check_executable
|
62
|
+
|
63
|
+
print_command
|
64
|
+
if options[:gem_name] == "gem" && options[:executable] == "gem"
|
65
|
+
set_gem_exec_install_paths
|
66
|
+
Gem::GemRunner.new.run options[:args]
|
67
|
+
return
|
68
|
+
elsif options[:conservative]
|
69
|
+
install_if_needed
|
70
|
+
else
|
71
|
+
install
|
72
|
+
activate!
|
73
|
+
end
|
74
|
+
|
75
|
+
load!
|
76
|
+
ensure
|
77
|
+
ENV.update(gem_paths) if gem_paths
|
78
|
+
Gem.clear_paths
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def handle_options(args)
|
84
|
+
args = add_extra_args(args)
|
85
|
+
check_deprecated_options(args)
|
86
|
+
@options = Marshal.load Marshal.dump @defaults # deep copy
|
87
|
+
parser.order!(args) do |v|
|
88
|
+
# put the non-option back at the front of the list of arguments
|
89
|
+
args.unshift(v)
|
90
|
+
|
91
|
+
# stop parsing once we hit the first non-option,
|
92
|
+
# so you can call `gem exec rails --version` and it prints the rails
|
93
|
+
# version rather than rubygem's
|
94
|
+
break
|
95
|
+
end
|
96
|
+
@options[:args] = args
|
97
|
+
|
98
|
+
options[:executable], gem_version = extract_gem_name_and_version(options[:args].shift)
|
99
|
+
options[:gem_name] ||= options[:executable]
|
100
|
+
|
101
|
+
if gem_version
|
102
|
+
if options[:version].none?
|
103
|
+
options[:version] = Gem::Requirement.new(gem_version)
|
104
|
+
else
|
105
|
+
options[:version].concat [gem_version]
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
if options[:prerelease] && !options[:version].prerelease?
|
110
|
+
if options[:version].none?
|
111
|
+
options[:version] = Gem::Requirement.default_prerelease
|
112
|
+
else
|
113
|
+
options[:version].concat [Gem::Requirement.default_prerelease]
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def check_executable
|
119
|
+
if options[:executable].nil?
|
120
|
+
raise Gem::CommandLineError,
|
121
|
+
"Please specify an executable to run (e.g. #{program_name} COMMAND)"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def print_command
|
126
|
+
verbose "running #{program_name} with:\n"
|
127
|
+
opts = options.reject {|_, v| v.nil? || Array(v).empty? }
|
128
|
+
max_length = opts.map {|k, _| k.size }.max
|
129
|
+
opts.each do |k, v|
|
130
|
+
next if v.nil?
|
131
|
+
verbose "\t#{k.to_s.rjust(max_length)}: #{v}"
|
132
|
+
end
|
133
|
+
verbose ""
|
134
|
+
end
|
135
|
+
|
136
|
+
def install_if_needed
|
137
|
+
activate!
|
138
|
+
rescue Gem::MissingSpecError
|
139
|
+
verbose "#{Gem::Dependency.new(options[:gem_name], options[:version])} not available locally, installing from remote"
|
140
|
+
install
|
141
|
+
activate!
|
142
|
+
end
|
143
|
+
|
144
|
+
def set_gem_exec_install_paths
|
145
|
+
home = File.join(Gem.dir, "gem_exec")
|
146
|
+
|
147
|
+
ENV["GEM_PATH"] = ([home] + Gem.path).join(File::PATH_SEPARATOR)
|
148
|
+
ENV["GEM_HOME"] = home
|
149
|
+
Gem.clear_paths
|
150
|
+
end
|
151
|
+
|
152
|
+
def install
|
153
|
+
set_gem_exec_install_paths
|
154
|
+
|
155
|
+
gem_name = options[:gem_name]
|
156
|
+
gem_version = options[:version]
|
157
|
+
|
158
|
+
install_options = options.merge(
|
159
|
+
minimal_deps: false,
|
160
|
+
wrappers: true
|
161
|
+
)
|
162
|
+
|
163
|
+
suppress_always_install do
|
164
|
+
dep_installer = Gem::DependencyInstaller.new install_options
|
165
|
+
|
166
|
+
request_set = dep_installer.resolve_dependencies gem_name, gem_version
|
167
|
+
|
168
|
+
verbose "Gems to install:"
|
169
|
+
request_set.sorted_requests.each do |activation_request|
|
170
|
+
verbose "\t#{activation_request.full_name}"
|
171
|
+
end
|
172
|
+
|
173
|
+
request_set.install install_options
|
174
|
+
end
|
175
|
+
|
176
|
+
Gem::Specification.reset
|
177
|
+
rescue Gem::InstallError => e
|
178
|
+
alert_error "Error installing #{gem_name}:\n\t#{e.message}"
|
179
|
+
terminate_interaction 1
|
180
|
+
rescue Gem::GemNotFoundException => e
|
181
|
+
show_lookup_failure e.name, e.version, e.errors, false
|
182
|
+
|
183
|
+
terminate_interaction 2
|
184
|
+
rescue Gem::UnsatisfiableDependencyError => e
|
185
|
+
show_lookup_failure e.name, e.version, e.errors, false,
|
186
|
+
"'#{gem_name}' (#{gem_version})"
|
187
|
+
|
188
|
+
terminate_interaction 2
|
189
|
+
end
|
190
|
+
|
191
|
+
def activate!
|
192
|
+
gem(options[:gem_name], options[:version])
|
193
|
+
Gem.finish_resolve
|
194
|
+
|
195
|
+
verbose "activated #{options[:gem_name]} (#{Gem.loaded_specs[options[:gem_name]].version})"
|
196
|
+
end
|
197
|
+
|
198
|
+
def load!
|
199
|
+
argv = ARGV.clone
|
200
|
+
ARGV.replace options[:args]
|
201
|
+
|
202
|
+
exe = executable = options[:executable]
|
203
|
+
|
204
|
+
contains_executable = Gem.loaded_specs.values.select do |spec|
|
205
|
+
spec.executables.include?(executable)
|
206
|
+
end
|
207
|
+
|
208
|
+
if contains_executable.any? {|s| s.name == executable }
|
209
|
+
contains_executable.select! {|s| s.name == executable }
|
210
|
+
end
|
211
|
+
|
212
|
+
if contains_executable.empty?
|
213
|
+
if (spec = Gem.loaded_specs[executable]) && (exe = spec.executable)
|
214
|
+
contains_executable << spec
|
215
|
+
else
|
216
|
+
alert_error "Failed to load executable `#{executable}`," \
|
217
|
+
" are you sure the gem `#{options[:gem_name]}` contains it?"
|
218
|
+
terminate_interaction 1
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
if contains_executable.size > 1
|
223
|
+
alert_error "Ambiguous which gem `#{executable}` should come from: " \
|
224
|
+
"the options are #{contains_executable.map(&:name)}, " \
|
225
|
+
"specify one via `-g`"
|
226
|
+
terminate_interaction 1
|
227
|
+
end
|
228
|
+
|
229
|
+
load Gem.activate_bin_path(contains_executable.first.name, exe, ">= 0.a")
|
230
|
+
ensure
|
231
|
+
ARGV.replace argv
|
232
|
+
end
|
233
|
+
|
234
|
+
def suppress_always_install
|
235
|
+
name = :always_install
|
236
|
+
cls = ::Gem::Resolver::InstallerSet
|
237
|
+
method = cls.instance_method(name)
|
238
|
+
cls.remove_method(name)
|
239
|
+
cls.define_method(name) { [] }
|
240
|
+
|
241
|
+
begin
|
242
|
+
yield
|
243
|
+
ensure
|
244
|
+
cls.remove_method(name)
|
245
|
+
cls.define_method(name, method)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
@@ -3,7 +3,7 @@ require_relative "../command"
|
|
3
3
|
|
4
4
|
class Gem::Commands::HelpCommand < Gem::Command
|
5
5
|
# :stopdoc:
|
6
|
-
EXAMPLES = <<-EOF
|
6
|
+
EXAMPLES = <<-EOF
|
7
7
|
Some examples of 'gem' usage.
|
8
8
|
|
9
9
|
* Install 'rake', either from local directory or remote server:
|
@@ -52,7 +52,7 @@ Some examples of 'gem' usage.
|
|
52
52
|
gem update --system
|
53
53
|
EOF
|
54
54
|
|
55
|
-
GEM_DEPENDENCIES = <<-EOF
|
55
|
+
GEM_DEPENDENCIES = <<-EOF
|
56
56
|
A gem dependencies file allows installation of a consistent set of gems across
|
57
57
|
multiple environments. The RubyGems implementation is designed to be
|
58
58
|
compatible with Bundler's Gemfile format. You can see additional
|
@@ -229,7 +229,7 @@ default. This may be overridden with the :development_group option:
|
|
229
229
|
|
230
230
|
EOF
|
231
231
|
|
232
|
-
PLATFORMS = <<-'EOF'
|
232
|
+
PLATFORMS = <<-'EOF'
|
233
233
|
RubyGems platforms are composed of three parts, a CPU, an OS, and a
|
234
234
|
version. These values are taken from values in rbconfig.rb. You can view
|
235
235
|
your current platform by running `gem environment`.
|