jwhitmire-geminstaller 0.5.4
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +1 -0
- data/History.txt +82 -0
- data/LICENSE +21 -0
- data/Manifest.txt +78 -0
- data/README.txt +77 -0
- data/Rakefile +210 -0
- data/TODO.txt +138 -0
- data/authors +2 -0
- data/bin/geminstaller +30 -0
- data/cruise_config.rb +23 -0
- data/focused_spec.sh +2 -0
- data/focused_spec_debug.sh +2 -0
- data/geminstaller.yml +47 -0
- data/lib/geminstaller/application.rb +112 -0
- data/lib/geminstaller/arg_parser.rb +177 -0
- data/lib/geminstaller/autogem.rb +48 -0
- data/lib/geminstaller/backward_compatibility.rb +17 -0
- data/lib/geminstaller/bundler_exporter.rb +19 -0
- data/lib/geminstaller/config.rb +68 -0
- data/lib/geminstaller/config_builder.rb +65 -0
- data/lib/geminstaller/enhanced_stream_ui.rb +71 -0
- data/lib/geminstaller/exact_match_list_command.rb +16 -0
- data/lib/geminstaller/file_reader.rb +31 -0
- data/lib/geminstaller/gem_arg_processor.rb +44 -0
- data/lib/geminstaller/gem_command_manager.rb +71 -0
- data/lib/geminstaller/gem_interaction_handler.rb +41 -0
- data/lib/geminstaller/gem_list_checker.rb +55 -0
- data/lib/geminstaller/gem_runner_proxy.rb +65 -0
- data/lib/geminstaller/gem_source_index_proxy.rb +21 -0
- data/lib/geminstaller/gem_spec_manager.rb +53 -0
- data/lib/geminstaller/geminstaller_access_error.rb +4 -0
- data/lib/geminstaller/geminstaller_error.rb +13 -0
- data/lib/geminstaller/hoe_extensions.rb +9 -0
- data/lib/geminstaller/install_processor.rb +71 -0
- data/lib/geminstaller/missing_dependency_finder.rb +46 -0
- data/lib/geminstaller/missing_file_error.rb +4 -0
- data/lib/geminstaller/noninteractive_chooser.rb +114 -0
- data/lib/geminstaller/outdated_gem_finder.rb +46 -0
- data/lib/geminstaller/output_filter.rb +49 -0
- data/lib/geminstaller/output_listener.rb +33 -0
- data/lib/geminstaller/output_observer.rb +36 -0
- data/lib/geminstaller/output_proxy.rb +36 -0
- data/lib/geminstaller/registry.rb +137 -0
- data/lib/geminstaller/requires.rb +83 -0
- data/lib/geminstaller/rogue_gem_finder.rb +195 -0
- data/lib/geminstaller/ruby_gem.rb +58 -0
- data/lib/geminstaller/rubygems_exit.rb +5 -0
- data/lib/geminstaller/rubygems_extensions.rb +9 -0
- data/lib/geminstaller/rubygems_version_checker.rb +21 -0
- data/lib/geminstaller/rubygems_version_warnings.rb +38 -0
- data/lib/geminstaller/source_index_search_adapter.rb +41 -0
- data/lib/geminstaller/unauthorized_dependency_prompt_error.rb +4 -0
- data/lib/geminstaller/unexpected_prompt_error.rb +4 -0
- data/lib/geminstaller/valid_platform_selector.rb +49 -0
- data/lib/geminstaller/version_specifier.rb +24 -0
- data/lib/geminstaller/yaml_loader.rb +22 -0
- data/lib/geminstaller.rb +103 -0
- data/lib/geminstaller_rails_preinitializer.rb +54 -0
- data/start_local_gem_server.sh +1 -0
- data/website/config.yaml +11 -0
- data/website/src/analytics.page +6 -0
- data/website/src/code/ci.virtual +5 -0
- data/website/src/code/coverage/index.virtual +5 -0
- data/website/src/code/index.page +92 -0
- data/website/src/code/rdoc/index.virtual +6 -0
- data/website/src/community/index.page +14 -0
- data/website/src/community/links.page +11 -0
- data/website/src/community/rubyforge.virtual +4 -0
- data/website/src/default.css +175 -0
- data/website/src/default.template +42 -0
- data/website/src/documentation/documentation.page +477 -0
- data/website/src/documentation/index.page +53 -0
- data/website/src/documentation/tutorials.page +337 -0
- data/website/src/download.page +12 -0
- data/website/src/faq.page +36 -0
- data/website/src/index.page +103 -0
- data/website/src/metainfo +54 -0
- data/website/src/webgen.css +112 -0
- metadata +139 -0
@@ -0,0 +1,31 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class FileReader
|
3
|
+
def read(file_path)
|
4
|
+
file_contents = nil
|
5
|
+
if !File.exist?(file_path) then
|
6
|
+
raise GemInstaller::MissingFileError.new("#{file_path}")
|
7
|
+
end
|
8
|
+
|
9
|
+
file = nil
|
10
|
+
begin
|
11
|
+
file = do_open(file_path)
|
12
|
+
rescue
|
13
|
+
raise GemInstaller::GemInstallerError.new("Error: Unable open file #{file_path}. Please ensure that this file can be opened.\n")
|
14
|
+
end
|
15
|
+
|
16
|
+
begin
|
17
|
+
do_read(file)
|
18
|
+
rescue
|
19
|
+
raise GemInstaller::GemInstallerError.new("Error: Unable read file #{file_path}. Please ensure that this file can be read.\n")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def do_open(file_path)
|
24
|
+
File.open(file_path)
|
25
|
+
end
|
26
|
+
|
27
|
+
def do_read(file)
|
28
|
+
file.read
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class GemArgProcessor
|
3
|
+
#Common Options:
|
4
|
+
# --source URL Use URL as the remote source for gems
|
5
|
+
# -p, --[no-]http-proxy [URL] Use HTTP proxy for remote operations
|
6
|
+
# -h, --help Get help on this command
|
7
|
+
# -v, --verbose Set the verbose level of output
|
8
|
+
# --config-file FILE Use this config file instead of default
|
9
|
+
# --backtrace Show stack backtrace on errors
|
10
|
+
# --debug Turn on Ruby debugging
|
11
|
+
GEM_COMMON_OPTIONS_WITHOUT_ARG = ['--no-http-proxy','-v','--verbose','--backtrace','--debug']
|
12
|
+
GEM_COMMON_OPTIONS_WITH_ARG = ['--source','-p','--http_proxy','--config-file']
|
13
|
+
|
14
|
+
# take an array of args, and strip all args that are not common gem command args
|
15
|
+
def strip_non_common_gem_args(args)
|
16
|
+
# I can't figure out a way to elegantly do this, so I hardcoded the options. Here's how you print them
|
17
|
+
# Gem::Command.common_options.each do |option|
|
18
|
+
# p option[0]
|
19
|
+
# end
|
20
|
+
|
21
|
+
common_args = []
|
22
|
+
i = 0
|
23
|
+
loop do
|
24
|
+
break if i == args.size
|
25
|
+
arg = args[i]
|
26
|
+
if GEM_COMMON_OPTIONS_WITHOUT_ARG.include?(arg)
|
27
|
+
common_args << arg
|
28
|
+
else
|
29
|
+
GEM_COMMON_OPTIONS_WITH_ARG.each do |option|
|
30
|
+
if arg.include?(option)
|
31
|
+
common_args << arg
|
32
|
+
unless arg.include?('=')
|
33
|
+
i += 1
|
34
|
+
common_args << args[i]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
i += 1
|
40
|
+
end
|
41
|
+
common_args
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class GemCommandManager
|
3
|
+
attr_writer :gem_spec_manager, :gem_runner_proxy
|
4
|
+
attr_writer :gem_interaction_handler if GemInstaller::RubyGemsVersionChecker.matches?('<=0.9.4')
|
5
|
+
|
6
|
+
def list_remote_gem(gem, additional_options)
|
7
|
+
run_args = ["list",gem.name,"--remote","--details"]
|
8
|
+
run_args << "--all" if GemInstaller::RubyGemsVersionChecker.matches?('>1.0.1')
|
9
|
+
run_args += additional_options
|
10
|
+
@gem_runner_proxy.run(run_args)
|
11
|
+
end
|
12
|
+
|
13
|
+
def uninstall_gem(gem)
|
14
|
+
return if !@gem_spec_manager.is_gem_installed?(gem)
|
15
|
+
init_gem_interaction_handler(gem)
|
16
|
+
run_gem_command('uninstall', gem, gem.uninstall_options)
|
17
|
+
end
|
18
|
+
|
19
|
+
def install_gem(gem, force_reinstall = false)
|
20
|
+
return [] if @gem_spec_manager.is_gem_installed?(gem) && !force_reinstall
|
21
|
+
init_gem_interaction_handler(gem)
|
22
|
+
run_gem_command('install', gem, gem.install_options)
|
23
|
+
end
|
24
|
+
|
25
|
+
def init_gem_interaction_handler(gem)
|
26
|
+
@gem_interaction_handler.dependent_gem = gem if GemInstaller::RubyGemsVersionChecker.matches?('<=0.9.4')
|
27
|
+
end
|
28
|
+
|
29
|
+
def dependency(name, version, additional_options = [])
|
30
|
+
# rubygems has bug up to 0.9.4 where the pipe options uses 'puts', instead of 'say', so we can't capture it
|
31
|
+
# with enhanced_stream_ui. Patched: http://rubyforge.org/tracker/index.php?func=detail&aid=9020&group_id=126&atid=577
|
32
|
+
# TODO: use pipe option on later versions which support it
|
33
|
+
|
34
|
+
name_regexp = "^#{name}$"
|
35
|
+
name_regexp = "/#{name_regexp}/" if GemInstaller::RubyGemsVersionChecker.matches?('>=1.2.0')
|
36
|
+
run_args = ["dependency", name_regexp, "--version", version]
|
37
|
+
run_args += additional_options
|
38
|
+
output_lines = @gem_runner_proxy.run(run_args)
|
39
|
+
# drop the first line which just echoes the dependent gem
|
40
|
+
output_lines.shift
|
41
|
+
# drop the line containing 'requires' (rubygems < 0.9.0)
|
42
|
+
if output_lines[0] == ' Requires'
|
43
|
+
output_lines.shift
|
44
|
+
end
|
45
|
+
# drop all empty lines
|
46
|
+
output_lines.reject! { |line| line == "" }
|
47
|
+
# strip leading space
|
48
|
+
output_lines.each { |line| line.strip! }
|
49
|
+
# convert into gems
|
50
|
+
output_gems = output_lines.collect do |line|
|
51
|
+
name = line.split(' ')[0]
|
52
|
+
version_spec = line.split(/[(,)]/)[1]
|
53
|
+
GemInstaller::RubyGem.new(name, :version => version_spec)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def run_gem_command(gem_command, gem, options)
|
58
|
+
run_args = [gem_command,gem.name,"--version", "#{gem.version}"]
|
59
|
+
if GemInstaller::RubyGemsVersionChecker.matches?('>=0.9.5')
|
60
|
+
run_args += ['--platform', "#{gem.platform}"] if gem.platform && !gem.platform.empty?
|
61
|
+
end
|
62
|
+
run_args += options
|
63
|
+
@gem_runner_proxy.run(run_args)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class GemInteractionHandler
|
3
|
+
attr_writer :dependent_gem, :noninteractive_chooser_class, :valid_platform_selector
|
4
|
+
DEPENDENCY_PROMPT = 'Install required dependency'
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
check_rubygems_version
|
8
|
+
end
|
9
|
+
|
10
|
+
def handle_ask_yes_no(question)
|
11
|
+
return unless question.index(DEPENDENCY_PROMPT)
|
12
|
+
message = "Error: RubyGems is prompting to install a required dependency, and you have not " +
|
13
|
+
"specified the '--install-dependencies' option for the current gem. You must modify your " +
|
14
|
+
"geminstaller config file to either specify the '--install-depencencies' (-y) " +
|
15
|
+
"option, or explicitly add an entry for the dependency gem earlier in the file.\n"
|
16
|
+
raise GemInstaller::UnauthorizedDependencyPromptError.new(message)
|
17
|
+
end
|
18
|
+
|
19
|
+
def handle_choose_from_list(question, list, noninteractive_chooser = nil)
|
20
|
+
noninteractive_chooser ||= @noninteractive_chooser_class.new
|
21
|
+
valid_platforms = nil
|
22
|
+
if dependent_gem_with_platform_specified?(list, noninteractive_chooser) or noninteractive_chooser.uninstall_list_type?(question)
|
23
|
+
valid_platforms = [@dependent_gem.platform]
|
24
|
+
else
|
25
|
+
valid_platforms = @valid_platform_selector.select(@dependent_gem.platform)
|
26
|
+
end
|
27
|
+
noninteractive_chooser.choose(question, list, @dependent_gem.name, @dependent_gem.version, valid_platforms)
|
28
|
+
end
|
29
|
+
|
30
|
+
def dependent_gem_with_platform_specified?(list, noninteractive_chooser)
|
31
|
+
noninteractive_chooser.dependent_gem?(@dependent_gem.name, list) and @dependent_gem.platform
|
32
|
+
end
|
33
|
+
|
34
|
+
def check_rubygems_version
|
35
|
+
if GemInstaller::RubyGemsVersionChecker.matches?('>=0.9.5')
|
36
|
+
# gem_interaction_handler is not used for RubyGems >= 0.9.5
|
37
|
+
raise RuntimeError.new("Internal GemInstaller Error: GemInteractionHandler should not be used for RubyGems >= 0.9.5")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class GemListChecker
|
3
|
+
attr_writer :gem_command_manager, :gem_arg_processor, :version_specifier
|
4
|
+
|
5
|
+
def find_remote_matching_gem(gem)
|
6
|
+
gem_list_match_regexp = /^#{gem.regexp_escaped_name} \(.*/
|
7
|
+
common_args = @gem_arg_processor.strip_non_common_gem_args(gem.install_options)
|
8
|
+
remote_list = @gem_command_manager.list_remote_gem(gem, common_args)
|
9
|
+
|
10
|
+
matched_lines = []
|
11
|
+
remote_list.each do |line|
|
12
|
+
if line.match(gem_list_match_regexp)
|
13
|
+
matched_lines << line
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
if matched_lines.size > 1
|
18
|
+
error_message = "Error: more than one remote gem matches (this should not happen and is probably a bug in geminstaller). Gem name = '#{gem.name}', install options = '#{gem.install_options.join(' ')}'. Matching remote gems: \n"
|
19
|
+
matched_lines.each do |line|
|
20
|
+
error_message += line + "\n"
|
21
|
+
end
|
22
|
+
raise GemInstaller::GemInstallerError.new(error_message)
|
23
|
+
end
|
24
|
+
|
25
|
+
if matched_lines.size == 0
|
26
|
+
error_message = "Error: Could not find remote gem to install. Gem name = '#{gem.name}', Gem version = '#{gem.version}', install options = '#{gem.install_options.join(' ')}'. Your remote gem server may be having problems. You should try to list the remote gem yourself: 'gem list -r -v '#{gem.version}' #{common_args.join(' ')} #{gem.name}'. If it exists on the server, try running geminstaller again and report your experience here: http://thewoolleyweb.lighthouseapp.com/projects/11580-geminstaller/tickets/5-sometimes-installing-fails. Output of remote gem list command: \n#{remote_list}"
|
27
|
+
raise GemInstaller::GemInstallerError.new(error_message)
|
28
|
+
end
|
29
|
+
|
30
|
+
return matched_lines[0]
|
31
|
+
end
|
32
|
+
|
33
|
+
def verify_and_specify_remote_gem!(gem)
|
34
|
+
# TODO: this seems like it is a hack, but we must have a non-ambiguous version on the gem in order for
|
35
|
+
# noninteractive_chooser to be able to parse the gem list for multi-platform gems. This will not be necessary
|
36
|
+
# if a future RubyGems release allows specification/searching of the platform, because then we won't need noninteractive_chooser
|
37
|
+
|
38
|
+
version_list = available_versions(gem)
|
39
|
+
specified_version = @version_specifier.specify(gem.version, version_list, gem.name)
|
40
|
+
gem.version = specified_version
|
41
|
+
end
|
42
|
+
|
43
|
+
def available_versions(gem)
|
44
|
+
remote_match_line = find_remote_matching_gem(gem)
|
45
|
+
version_list = parse_out_version_list(remote_match_line)
|
46
|
+
end
|
47
|
+
|
48
|
+
def parse_out_version_list(line)
|
49
|
+
# return everything between first set of parenthesis
|
50
|
+
version_list = line.split(/[()]/)[1]
|
51
|
+
version_list
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class GemRunnerProxy
|
3
|
+
attr_writer :gem_runner_class, :gem_cmd_manager_class, :output_listener, :exact_match_list_command
|
4
|
+
attr_writer :options, :enhanced_stream_ui, :output_filter
|
5
|
+
|
6
|
+
def run(args = [], stdin = [])
|
7
|
+
@output_filter.geminstaller_output(:commandecho,"'gem #{args.join(' ')}'\n")
|
8
|
+
gem_runner = create_gem_runner
|
9
|
+
# We have to manually initialize the configuration here, or else the GemCommandManager singleton
|
10
|
+
# will initialize with the (incorrect) default args when we call GemRunner.run.
|
11
|
+
gem_runner.do_configuration(args)
|
12
|
+
gem_cmd_manager = @gem_cmd_manager_class.instance
|
13
|
+
gem_cmd_manager.ui = @enhanced_stream_ui
|
14
|
+
gem_cmd_manager.commands[:list] = @exact_match_list_command
|
15
|
+
|
16
|
+
exit_status = nil
|
17
|
+
begin
|
18
|
+
# The call below is to work around RubyGem's behavior of caching (only) the last-used source when
|
19
|
+
# multiple gem commands for gems on different sources are issued via the API
|
20
|
+
Gem.sources.replace Gem.default_sources
|
21
|
+
gem_runner.run(args)
|
22
|
+
rescue SystemExit => system_exit
|
23
|
+
if GemInstaller::RubyGemsVersionChecker.matches?('>1.0.1')
|
24
|
+
raise system_exit unless system_exit.is_a? Gem::SystemExitException
|
25
|
+
exit_status = system_exit.message
|
26
|
+
else
|
27
|
+
raise system_exit
|
28
|
+
end
|
29
|
+
rescue GemInstaller::RubyGemsExit => normal_exit
|
30
|
+
exit_status = normal_exit.message
|
31
|
+
rescue GemInstaller::GemInstallerError => exit_error
|
32
|
+
raise_error_with_output(exit_error, args, @output_listener)
|
33
|
+
end
|
34
|
+
output_lines = @output_listener.read!
|
35
|
+
output_lines.push(exit_status) if exit_status
|
36
|
+
output_lines.collect do |line|
|
37
|
+
line.split("\n")
|
38
|
+
end.flatten
|
39
|
+
end
|
40
|
+
|
41
|
+
def create_gem_runner
|
42
|
+
if GemInstaller::RubyGemsVersionChecker.matches?('< 0.9')
|
43
|
+
@gem_runner_class.new()
|
44
|
+
else
|
45
|
+
@gem_runner_class.new(:command_manager => @gem_cmd_manager_class)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def raise_error_with_output(exit_error, args, listener)
|
50
|
+
error_class = exit_error.class
|
51
|
+
error_message = exit_error.message
|
52
|
+
gem_command_output = listener.read!
|
53
|
+
if gem_command_output.join('') =~ /Errno::EACCES/
|
54
|
+
error_message = access_error_message + "\n" + error_message
|
55
|
+
error_class = GemInstaller::GemInstallerAccessError
|
56
|
+
end
|
57
|
+
descriptive_exit_message = exit_error.descriptive_exit_message(error_message, 'gem', args, gem_command_output)
|
58
|
+
raise error_class.new(descriptive_exit_message)
|
59
|
+
end
|
60
|
+
|
61
|
+
def access_error_message
|
62
|
+
"You don't have permission to install a gem.\nThis is not a problem with GemInstaller.\nYou probably want use the --sudo option or run GemInstaller as sudo,\nor install your gems to a non-root-owned location.\nSee http://geminstaller.rubyforge.org/documentation/documentation.html#dealing_with_sudo\n"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class GemSourceIndexProxy
|
3
|
+
attr_writer :gem_source_index
|
4
|
+
|
5
|
+
def refresh!
|
6
|
+
@gem_source_index.refresh!
|
7
|
+
end
|
8
|
+
|
9
|
+
# NOTE: We will require an exact version requirement, rather than the standard default Gem version_requirement of ">= 0"
|
10
|
+
# GemInstaller has it's own default defined elsewhere
|
11
|
+
def search(gem_pattern, version_requirement)
|
12
|
+
# Returns an array of Gem::Specification objects
|
13
|
+
@gem_source_index.search(gem_pattern, version_requirement)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class GemSpecManager
|
3
|
+
attr_writer :source_index_search_adapter, :valid_platform_selector, :output_filter
|
4
|
+
|
5
|
+
def is_gem_installed?(gem)
|
6
|
+
return local_matching_gems(gem).size > 0
|
7
|
+
end
|
8
|
+
|
9
|
+
def all_local_gems
|
10
|
+
all_local_specs = @source_index_search_adapter.all_local_specs
|
11
|
+
return [] unless all_local_specs
|
12
|
+
all_local_gems = all_local_specs.collect do |spec|
|
13
|
+
gem = GemInstaller::RubyGem.new(spec.name, :version => spec.version.version)
|
14
|
+
end
|
15
|
+
return all_local_gems
|
16
|
+
end
|
17
|
+
|
18
|
+
def local_matching_gems(gem, exact_platform_match = true)
|
19
|
+
found_gem_specs = @source_index_search_adapter.search(gem,gem.version)
|
20
|
+
return [] unless found_gem_specs
|
21
|
+
|
22
|
+
if GemInstaller::RubyGemsVersionChecker.matches?('<=0.9.4')
|
23
|
+
found_gem_specs = found_gem_specs.select do |gem_spec|
|
24
|
+
valid_platforms = @valid_platform_selector.select(gem.platform, exact_platform_match)
|
25
|
+
valid_platforms.include?(gem_spec.platform)
|
26
|
+
end
|
27
|
+
else
|
28
|
+
# TODO: this is a hack because source_index#search doesn't allow specification of platform
|
29
|
+
if exact_platform_match
|
30
|
+
found_gem_specs = found_gem_specs.select do |spec|
|
31
|
+
spec_platform_matches?(spec, gem.platform)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
matching_gems = found_gem_specs.map do |gem_spec|
|
36
|
+
GemInstaller::RubyGem.new(gem_spec.name, {:version => gem_spec.version.version, :platform => gem_spec.platform })
|
37
|
+
end
|
38
|
+
return matching_gems
|
39
|
+
end
|
40
|
+
|
41
|
+
def spec_platform_matches?(spec, platform)
|
42
|
+
if GemInstaller::RubyGemsVersionChecker.matches?('>0.9.5')
|
43
|
+
return Gem::Platform.new(spec.platform) == Gem::Platform.new(platform)
|
44
|
+
end
|
45
|
+
return spec.platform == platform
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class GemInstallerError < RuntimeError
|
3
|
+
def descriptive_exit_message(message, command, args, gem_command_output)
|
4
|
+
args_string = args.join(" ")
|
5
|
+
descriptive_exit_message = "\n=========================================================\n"
|
6
|
+
descriptive_exit_message += "#{message}\n"
|
7
|
+
descriptive_exit_message += "Gem command was:\n #{command} #{args_string}\n\n"
|
8
|
+
descriptive_exit_message += "Gem command output was:\n"
|
9
|
+
descriptive_exit_message += gem_command_output.join("\n")
|
10
|
+
descriptive_exit_message += "\n=========================================================\n\n"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class InstallProcessor
|
3
|
+
attr_writer :gem_list_checker, :gem_command_manager, :gem_spec_manager, :missing_dependency_finder, :options, :output_filter
|
4
|
+
def process(gems)
|
5
|
+
gems.each do |gem|
|
6
|
+
install_gem(gem)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def install_gem(gem)
|
11
|
+
already_specified = false
|
12
|
+
if gem.check_for_upgrade
|
13
|
+
@gem_list_checker.verify_and_specify_remote_gem!(gem)
|
14
|
+
already_specified = true
|
15
|
+
end
|
16
|
+
gem_is_installed = @gem_spec_manager.is_gem_installed?(gem)
|
17
|
+
if gem_is_installed
|
18
|
+
@output_filter.geminstaller_output(:debug,"Gem #{gem.name}, version #{gem.version} is already installed.\n")
|
19
|
+
else
|
20
|
+
perform_install(gem, already_specified)
|
21
|
+
installation_performed = true
|
22
|
+
end
|
23
|
+
if gem.fix_dependencies
|
24
|
+
if GemInstaller::RubyGemsVersionChecker.matches?('>=0.9.5')
|
25
|
+
# RubyGems >=0.9.5 automatically handles missing dependencies, so just perform an install
|
26
|
+
unless installation_performed
|
27
|
+
@output_filter.geminstaller_output(:install,"The 'fix_dependencies' option was specified for #{gem.name}, version #{gem.version}, so it will be reinstalled to fix any missing dependencies.\n")
|
28
|
+
perform_install(gem, already_specified, true)
|
29
|
+
end
|
30
|
+
else
|
31
|
+
# RubyGems <=0.9.4 does not automatically handles missing dependencies, so GemInstaller must find them
|
32
|
+
# manually with missing_dependency_finder
|
33
|
+
fix_dependencies(gem)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def perform_install(gem, already_specified, force_install = false)
|
39
|
+
@gem_list_checker.verify_and_specify_remote_gem!(gem) unless already_specified
|
40
|
+
@output_filter.geminstaller_output(:install,"Invoking gem install for #{gem.name}, version #{gem.version}.\n")
|
41
|
+
output_lines = @gem_command_manager.install_gem(gem, force_install)
|
42
|
+
print_dependency_install_messages(gem, output_lines) unless @options[:silent]
|
43
|
+
end
|
44
|
+
|
45
|
+
def print_dependency_install_messages(gem, output_lines)
|
46
|
+
output_lines.each do |line|
|
47
|
+
line =~ /Successfully installed /
|
48
|
+
match = $'
|
49
|
+
next unless match
|
50
|
+
next if match =~ /#{gem.name}-/
|
51
|
+
@output_filter.geminstaller_output(:install,"Rubygems automatically installed dependency gem #{match}\n")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def fix_dependencies(gem)
|
56
|
+
missing_dependencies = @missing_dependency_finder.find(gem)
|
57
|
+
while (missing_dependencies.size > 0)
|
58
|
+
missing_dependencies.each do |missing_dependency|
|
59
|
+
@output_filter.geminstaller_output(:install,"Installing #{missing_dependency.name} (#{missing_dependency.version})\n")
|
60
|
+
# recursively call install_gem to install the missing dependency. Since fix_dependencies
|
61
|
+
# should never be set on an auto-created missing dependency gem, there is no risk of an
|
62
|
+
# endless loop or stack overflow.
|
63
|
+
install_gem(missing_dependency)
|
64
|
+
end
|
65
|
+
# continue to look for and install missing dependencies until none are found
|
66
|
+
missing_dependencies = @missing_dependency_finder.find(gem)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class MissingDependencyFinder
|
3
|
+
attr_writer :gem_command_manager, :gem_spec_manager, :gem_arg_processor, :output_filter
|
4
|
+
def find(dependent_gem)
|
5
|
+
if GemInstaller::RubyGemsVersionChecker.matches?('>=0.9.5')
|
6
|
+
# missing_dependency_finder is not used for RubyGems >= 0.9.5
|
7
|
+
raise RuntimeError.new("Internal GemInstaller Error: MissingDependencyFinder should not be used for RubyGems >= 0.9.5")
|
8
|
+
end
|
9
|
+
|
10
|
+
# NOTE: this doesn't resolve platforms, there's currently no way to know what
|
11
|
+
# platform should be selected for a dependency gem. Best-effort handling
|
12
|
+
# of ambiguous platforms on dependency gems will be handled elsewhere
|
13
|
+
matching_dependent_gems = @gem_spec_manager.local_matching_gems(dependent_gem)
|
14
|
+
missing_dependencies = []
|
15
|
+
install_options = dependent_gem.install_options
|
16
|
+
add_include_dependency_option(install_options)
|
17
|
+
common_args = @gem_arg_processor.strip_non_common_gem_args(install_options)
|
18
|
+
matching_dependent_gems.each do |matching_dependent_gem|
|
19
|
+
message_already_printed = false
|
20
|
+
dependency_gems = @gem_command_manager.dependency(matching_dependent_gem.name, matching_dependent_gem.version.to_s, common_args)
|
21
|
+
dependency_gems.each do |dependency_gem|
|
22
|
+
dependency_gem.install_options = install_options
|
23
|
+
local_matching_dependency_gems = @gem_spec_manager.local_matching_gems(dependency_gem, false)
|
24
|
+
unless local_matching_dependency_gems.size > 0
|
25
|
+
unless message_already_printed
|
26
|
+
@output_filter.geminstaller_output(:info, "Missing dependencies found for #{matching_dependent_gem.name} (#{matching_dependent_gem.version}):\n")
|
27
|
+
message_already_printed = true
|
28
|
+
end
|
29
|
+
# TODO: print install options too?
|
30
|
+
@output_filter.geminstaller_output(:info, " #{dependency_gem.name} (#{dependency_gem.version})\n")
|
31
|
+
missing_dependencies << dependency_gem
|
32
|
+
end
|
33
|
+
# recurse to find any missing dependencies in the tree
|
34
|
+
sub_dependencies = find(dependency_gem)
|
35
|
+
missing_dependencies += sub_dependencies if sub_dependencies.size > 0
|
36
|
+
end
|
37
|
+
end
|
38
|
+
return missing_dependencies
|
39
|
+
end
|
40
|
+
|
41
|
+
def add_include_dependency_option(install_options)
|
42
|
+
return if install_options.index('-y') or install_options.index('--include-dependencies')
|
43
|
+
install_options << '--include-dependencies'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
# Format for "install" prompt list: "#{spec.name} #{spec.version} (#{spec.platform})"
|
3
|
+
# Format for "uninstall" prompt list: "#{spec.name}-#{spec.version}" for ruby, "#{spec.name}-#{spec.version}-#{spec.platform}" (gem.full_name) for binary
|
4
|
+
class NoninteractiveChooser
|
5
|
+
def initialize
|
6
|
+
check_rubygems_version
|
7
|
+
@question = nil
|
8
|
+
end
|
9
|
+
|
10
|
+
def choose(question, list, dependent_gem_name, dependent_gem_version, valid_platforms)
|
11
|
+
@question = question
|
12
|
+
@list = list
|
13
|
+
@dependent_gem_name = dependent_gem_name
|
14
|
+
@dependent_gem_version = dependent_gem_version
|
15
|
+
@valid_platforms = valid_platforms
|
16
|
+
raise GemInstaller::GemInstallerError.new("Internal GemInstaller Error, unexpected choice prompt question: '#{question}'") unless
|
17
|
+
install_list_type? or uninstall_list_type?
|
18
|
+
|
19
|
+
raise GemInstaller::GemInstallerError.new("valid_platforms must be passed as an array.") unless
|
20
|
+
valid_platforms.respond_to? :shift
|
21
|
+
|
22
|
+
raise GemInstaller::GemInstallerError.new("Internal GemInstaller Error, multiple platforms cannot be specified for an uninstall prompt") if
|
23
|
+
uninstall_list_type? and valid_platforms.size > 1
|
24
|
+
|
25
|
+
index = nil
|
26
|
+
valid_platforms.each do |platform|
|
27
|
+
index = find_matching_line(platform)
|
28
|
+
break if index
|
29
|
+
end
|
30
|
+
|
31
|
+
return @list[index], index if index
|
32
|
+
|
33
|
+
# if we didn't find a match, raise a descriptive error
|
34
|
+
action = install_list_type? ? 'install' : 'uninstall'
|
35
|
+
name = @dependent_gem_name ? "#{@dependent_gem_name}" : "'any name'"
|
36
|
+
version = @dependent_gem_version ? "#{@dependent_gem_version}" : "'any version'"
|
37
|
+
platform = @valid_platforms ? " (#{@valid_platforms.join(' or ')})" : ''
|
38
|
+
error_message = "Error: Unable to select gem from list: \"#{name} #{version}#{platform}\". Available gems are:\n"
|
39
|
+
list.each do |item|
|
40
|
+
# skip last 'cancel' list entry, it's not a real gem
|
41
|
+
next if item == list.last
|
42
|
+
error_message += " " + item + "\n"
|
43
|
+
end
|
44
|
+
raise GemInstaller::GemInstallerError.new(error_message)
|
45
|
+
end
|
46
|
+
|
47
|
+
def find_matching_line(platform)
|
48
|
+
required_list_item_regexp = required_name_and_version_regexp + required_platform_regexp(platform)
|
49
|
+
line_selector = /^#{required_list_item_regexp}$/
|
50
|
+
@list.each_with_index do |item, index|
|
51
|
+
if item =~ line_selector
|
52
|
+
return index
|
53
|
+
end
|
54
|
+
end
|
55
|
+
return nil
|
56
|
+
end
|
57
|
+
|
58
|
+
def required_name_and_version_regexp
|
59
|
+
required_name_regexp = ".*?"
|
60
|
+
required_version_regexp = ".*?"
|
61
|
+
if uninstall_list_type? or dependent_gem?
|
62
|
+
# match the exact name and version if it's an uninstall prompt or a dependent gem
|
63
|
+
required_name_regexp = Regexp.escape(@dependent_gem_name) if @dependent_gem_name
|
64
|
+
required_version_regexp = Regexp.escape(@dependent_gem_version) if @dependent_gem_version
|
65
|
+
end
|
66
|
+
"#{required_name_regexp}[\s-]{0,1}#{required_version_regexp}"
|
67
|
+
end
|
68
|
+
|
69
|
+
def required_platform_regexp(platform_to_match)
|
70
|
+
return "" unless platform_to_match
|
71
|
+
required_platform = ""
|
72
|
+
escaped_platform_to_match = Regexp.escape(platform_to_match)
|
73
|
+
platform_regex = nil
|
74
|
+
if dependent_gem?
|
75
|
+
# do an exact match on the platform if this is the dependent gem
|
76
|
+
platform_regex = "#{escaped_platform_to_match}"
|
77
|
+
else
|
78
|
+
# do a wildcard match on the platform if it's not the dependent gem
|
79
|
+
platform_regex = ".*?#{escaped_platform_to_match}.*?"
|
80
|
+
end
|
81
|
+
# install list types always have the platform for each gem in parenthesis, even if it is ruby
|
82
|
+
if (install_list_type?)
|
83
|
+
required_platform = " \\(#{platform_regex}\\)"
|
84
|
+
end
|
85
|
+
# uninstall list types have the gem full_name, which is the platform for each gem appended after a dash, but only if it is not ruby
|
86
|
+
if (uninstall_list_type? && platform_to_match.to_s != GemInstaller::RubyGem.default_platform)
|
87
|
+
required_platform = "-.*?#{platform_regex}.*?"
|
88
|
+
end
|
89
|
+
required_platform
|
90
|
+
end
|
91
|
+
|
92
|
+
def install_list_type?(question = @question)
|
93
|
+
question =~ /to install/
|
94
|
+
end
|
95
|
+
|
96
|
+
def uninstall_list_type?(question = @question)
|
97
|
+
question =~ /to uninstall/
|
98
|
+
end
|
99
|
+
|
100
|
+
def dependent_gem?(dependent_gem_name = @dependent_gem_name, list = @list)
|
101
|
+
# return true if it's an install prompt, and the list contains the gem for which the original install request
|
102
|
+
# was made (in other words, it's a dependent gem, not a dependency gem)
|
103
|
+
install_format_exact_name_match_regexp = /^#{dependent_gem_name}\s.*/
|
104
|
+
install_list_type? and list[0] =~ install_format_exact_name_match_regexp
|
105
|
+
end
|
106
|
+
|
107
|
+
def check_rubygems_version
|
108
|
+
if GemInstaller::RubyGemsVersionChecker.matches?('>=0.9.5')
|
109
|
+
# noninteractive_chooser is not used for RubyGems >= 0.9.5
|
110
|
+
raise RuntimeError.new("Internal GemInstaller Error: NoninteractiveChooser should not be used for RubyGems >= 0.9.5")
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|