geminstaller 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/COPYING +1 -0
- data/History.txt +4 -0
- data/LICENSE +21 -0
- data/Manifest.txt +62 -0
- data/README.txt +60 -0
- data/Rakefile +92 -0
- data/TODO.txt +88 -0
- data/bin/geminstaller +30 -0
- data/geminstaller.yml +34 -0
- data/lib/geminstaller/application.rb +105 -0
- data/lib/geminstaller/arg_parser.rb +162 -0
- data/lib/geminstaller/autogem.rb +43 -0
- data/lib/geminstaller/config.rb +69 -0
- data/lib/geminstaller/config_builder.rb +40 -0
- data/lib/geminstaller/dependency_injector.rb +129 -0
- data/lib/geminstaller/enhanced_stream_ui.rb +57 -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_line_proxy.rb +32 -0
- data/lib/geminstaller/gem_command_manager.rb +62 -0
- data/lib/geminstaller/gem_interaction_handler.rb +30 -0
- data/lib/geminstaller/gem_list_checker.rb +55 -0
- data/lib/geminstaller/gem_runner_proxy.rb +43 -0
- data/lib/geminstaller/gem_source_index_proxy.rb +21 -0
- data/lib/geminstaller/gem_spec_manager.rb +42 -0
- data/lib/geminstaller/geminstaller_error.rb +13 -0
- data/lib/geminstaller/hoe_extensions.rb +5 -0
- data/lib/geminstaller/install_processor.rb +56 -0
- data/lib/geminstaller/missing_dependency_finder.rb +41 -0
- data/lib/geminstaller/missing_file_error.rb +4 -0
- data/lib/geminstaller/noninteractive_chooser.rb +107 -0
- data/lib/geminstaller/output_filter.rb +49 -0
- data/lib/geminstaller/output_listener.rb +33 -0
- data/lib/geminstaller/output_observer.rb +32 -0
- data/lib/geminstaller/output_proxy.rb +36 -0
- data/lib/geminstaller/requires.rb +59 -0
- data/lib/geminstaller/rogue_gem_finder.rb +195 -0
- data/lib/geminstaller/ruby_gem.rb +44 -0
- data/lib/geminstaller/rubygems_exit.rb +5 -0
- data/lib/geminstaller/rubygems_extensions.rb +5 -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 +44 -0
- data/lib/geminstaller/version_specifier.rb +24 -0
- data/lib/geminstaller/yaml_loader.rb +22 -0
- data/lib/geminstaller.rb +102 -0
- data/start_local_gem_server.sh +1 -0
- data/test/test_all.rb +31 -0
- data/website/config.yaml +2 -0
- data/website/metainfo.yaml +72 -0
- data/website/src/code/index.page +25 -0
- data/website/src/community/index.page +14 -0
- data/website/src/community/links.page +9 -0
- data/website/src/default.css +168 -0
- data/website/src/default.template +41 -0
- data/website/src/documentation/documentation.page +417 -0
- data/website/src/documentation/index.page +88 -0
- data/website/src/documentation/tutorials.page +94 -0
- data/website/src/download.page +12 -0
- data/website/src/faq.page +7 -0
- data/website/src/index.page +38 -0
- metadata +107 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class Autogem
|
3
|
+
attr_writer :gem_command_manager, :gem_spec_manager, :gem_source_index_proxy
|
4
|
+
def autogem(gems)
|
5
|
+
@gem_source_index_proxy.refresh!
|
6
|
+
reversed_gems = gems.reverse!
|
7
|
+
@completed_names = []
|
8
|
+
@completed_gems = []
|
9
|
+
reversed_gems.each do |gem|
|
10
|
+
process_gem(gem)
|
11
|
+
end
|
12
|
+
@completed_gems
|
13
|
+
end
|
14
|
+
|
15
|
+
def process_gem(gem)
|
16
|
+
unless @completed_names.index(gem.name) or gem.no_autogem
|
17
|
+
invoke_require_gem_command(gem.name, gem.version)
|
18
|
+
@completed_names << gem.name
|
19
|
+
@completed_gems << gem
|
20
|
+
end
|
21
|
+
process_gem_dependencies(gem)
|
22
|
+
end
|
23
|
+
|
24
|
+
def process_gem_dependencies(gem)
|
25
|
+
# TODO: this method is duplicated in rogue_gem_finder. Should abstract and take a block
|
26
|
+
matching_gems = @gem_spec_manager.local_matching_gems(gem)
|
27
|
+
matching_gems.each do |matching_gem|
|
28
|
+
dependency_gems = @gem_command_manager.dependency(matching_gem.name, matching_gem.version.to_s)
|
29
|
+
dependency_gems.each do |dependency_gem|
|
30
|
+
process_gem(dependency_gem)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def invoke_require_gem_command(name, version)
|
36
|
+
if Gem::RubyGemsVersion.index('0.8') == 0
|
37
|
+
require_gem(name, version)
|
38
|
+
else
|
39
|
+
gem(name, version)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class Config
|
3
|
+
def initialize(yaml)
|
4
|
+
@yaml = yaml
|
5
|
+
@default_install_options_string = nil
|
6
|
+
end
|
7
|
+
|
8
|
+
def gems
|
9
|
+
parse_defaults
|
10
|
+
gem_defs = @yaml["gems"]
|
11
|
+
gems = []
|
12
|
+
gem_defs.each do |gem_def|
|
13
|
+
name = gem_def['name']
|
14
|
+
version = gem_def['version'].to_s
|
15
|
+
platform = gem_def['platform'].to_s
|
16
|
+
# get install_options for specific gem, if specified
|
17
|
+
install_options_string = gem_def['install_options'].to_s
|
18
|
+
# if no install_options were specified for specific gem, and default install_options were specified...
|
19
|
+
if install_options_string.empty? &&
|
20
|
+
!@default_install_options_string.nil? &&
|
21
|
+
!@default_install_options_string.empty? then
|
22
|
+
# then use the default install_options
|
23
|
+
install_options_string = @default_install_options_string
|
24
|
+
end
|
25
|
+
install_options_array = []
|
26
|
+
# if there was an install options string specified, default or gem-specific, parse it to an array
|
27
|
+
install_options_array = install_options_string.split(" ") unless install_options_string.empty?
|
28
|
+
|
29
|
+
check_for_upgrade = gem_def['check_for_upgrade']
|
30
|
+
if check_for_upgrade.nil? && defined? @default_check_for_upgrade then
|
31
|
+
check_for_upgrade = @default_check_for_upgrade
|
32
|
+
end
|
33
|
+
|
34
|
+
fix_dependencies = gem_def['fix_dependencies']
|
35
|
+
if fix_dependencies.nil? && defined? @default_fix_dependencies then
|
36
|
+
fix_dependencies = @default_fix_dependencies
|
37
|
+
end
|
38
|
+
|
39
|
+
no_autogem = gem_def['no_autogem']
|
40
|
+
if no_autogem.nil? && defined? @default_no_autogem then
|
41
|
+
no_autogem = @default_no_autogem
|
42
|
+
end
|
43
|
+
|
44
|
+
gem = GemInstaller::RubyGem.new(
|
45
|
+
name,
|
46
|
+
:version => version,
|
47
|
+
:platform => platform,
|
48
|
+
:install_options => install_options_array,
|
49
|
+
:check_for_upgrade => check_for_upgrade,
|
50
|
+
:no_autogem => no_autogem,
|
51
|
+
:fix_dependencies => fix_dependencies)
|
52
|
+
gems << gem
|
53
|
+
end
|
54
|
+
gems.sort!
|
55
|
+
return gems
|
56
|
+
end
|
57
|
+
|
58
|
+
protected
|
59
|
+
|
60
|
+
def parse_defaults
|
61
|
+
defaults = @yaml["defaults"]
|
62
|
+
return if defaults.nil?
|
63
|
+
@default_install_options_string = defaults['install_options']
|
64
|
+
@default_check_for_upgrade = defaults['check_for_upgrade']
|
65
|
+
@default_fix_dependencies = defaults['fix_dependencies']
|
66
|
+
@default_no_autogem = defaults['no_autogem']
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
dir = File.dirname(__FILE__)
|
2
|
+
require File.expand_path("#{dir}/requires.rb")
|
3
|
+
|
4
|
+
module GemInstaller
|
5
|
+
class ConfigBuilder
|
6
|
+
attr_reader :config_file_paths_array
|
7
|
+
attr_writer :file_reader
|
8
|
+
attr_writer :yaml_loader
|
9
|
+
attr_writer :config_file_paths
|
10
|
+
|
11
|
+
def self.default_config_file_path
|
12
|
+
'geminstaller.yml'
|
13
|
+
end
|
14
|
+
|
15
|
+
def build_config
|
16
|
+
@config_file_paths ||= GemInstaller::ConfigBuilder.default_config_file_path
|
17
|
+
@config_file_paths_array = @config_file_paths.split(",")
|
18
|
+
merged_defaults = {}
|
19
|
+
merged_gems = {}
|
20
|
+
@config_file_paths_array.each do |path|
|
21
|
+
file_contents = @file_reader.read(path)
|
22
|
+
next unless file_contents
|
23
|
+
next if file_contents.empty?
|
24
|
+
file_contents_erb = ERB.new(%{#{file_contents}})
|
25
|
+
yaml = @yaml_loader.load(file_contents_erb.result)
|
26
|
+
merged_defaults.merge!(yaml['defaults']) unless yaml['defaults'].nil?
|
27
|
+
next unless yaml['gems']
|
28
|
+
yaml['gems'].each do |gem|
|
29
|
+
gem_key = [gem['name'],gem['version'],gem['platform']].join('-')
|
30
|
+
merged_gems[gem_key] = gem
|
31
|
+
end
|
32
|
+
end
|
33
|
+
merged_yaml = {}
|
34
|
+
merged_yaml['defaults'] = merged_defaults
|
35
|
+
merged_yaml['gems'] = merged_gems.values
|
36
|
+
config = GemInstaller::Config.new(merged_yaml)
|
37
|
+
config
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class DependencyInjector
|
3
|
+
def registry
|
4
|
+
@registry ||= create_registry
|
5
|
+
end
|
6
|
+
|
7
|
+
def create_registry
|
8
|
+
# define the service registry
|
9
|
+
@registry = GemInstaller::Registry.new
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Registry
|
14
|
+
attr_accessor :file_reader, :yaml_loader, :output_proxy, :config_builder, :gem_source_index, :gem_runner_proxy
|
15
|
+
attr_accessor :gem_runner, :gem_command_manager, :gem_list_checker, :app, :arg_parser, :options, :noninteractive_chooser
|
16
|
+
attr_accessor :gem_interaction_handler, :install_processor, :missing_dependency_finder, :valid_platform_selector
|
17
|
+
attr_accessor :output_listener, :outs_output_observer, :errs_output_observer, :output_filter, :autogem, :rogue_gem_finder
|
18
|
+
attr_accessor :gem_spec_manager
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@options = {}
|
22
|
+
@arg_parser = GemInstaller::ArgParser.new
|
23
|
+
@arg_parser.options = @options
|
24
|
+
|
25
|
+
@file_reader = GemInstaller::FileReader.new
|
26
|
+
@yaml_loader = GemInstaller::YamlLoader.new
|
27
|
+
@gem_arg_processor = GemInstaller::GemArgProcessor.new
|
28
|
+
@version_specifier = GemInstaller::VersionSpecifier.new
|
29
|
+
@output_proxy = GemInstaller::OutputProxy.new
|
30
|
+
@output_proxy.options = @options
|
31
|
+
@output_filter = GemInstaller::OutputFilter.new
|
32
|
+
@output_filter.options = @options
|
33
|
+
@output_filter.output_proxy = @output_proxy
|
34
|
+
|
35
|
+
|
36
|
+
@output_listener = GemInstaller::OutputListener.new
|
37
|
+
@output_listener.output_filter = @output_filter
|
38
|
+
|
39
|
+
@valid_platform_selector = GemInstaller::ValidPlatformSelector.new
|
40
|
+
@valid_platform_selector.output_filter = @output_filter
|
41
|
+
@valid_platform_selector.options = @options
|
42
|
+
|
43
|
+
@config_builder = GemInstaller::ConfigBuilder.new
|
44
|
+
@config_builder.file_reader = @file_reader
|
45
|
+
@config_builder.yaml_loader = @yaml_loader
|
46
|
+
|
47
|
+
@gem_source_index = Gem::SourceIndex.new
|
48
|
+
@gem_source_index_proxy = GemInstaller::GemSourceIndexProxy.new
|
49
|
+
@gem_source_index_proxy.gem_source_index = @gem_source_index
|
50
|
+
|
51
|
+
@gem_interaction_handler = GemInstaller::GemInteractionHandler.new
|
52
|
+
@noninteractive_chooser_class = GemInstaller::NoninteractiveChooser
|
53
|
+
@gem_interaction_handler.noninteractive_chooser_class = @noninteractive_chooser_class
|
54
|
+
@gem_interaction_handler.valid_platform_selector = @valid_platform_selector
|
55
|
+
|
56
|
+
@outs_output_observer = GemInstaller::OutputObserver.new
|
57
|
+
@outs_output_observer.stream = :stdout
|
58
|
+
@outs_output_observer.register(@output_listener)
|
59
|
+
@errs_output_observer = GemInstaller::OutputObserver.new
|
60
|
+
@errs_output_observer.stream = :stderr
|
61
|
+
@errs_output_observer.register(@output_listener)
|
62
|
+
|
63
|
+
@enhanced_stream_ui = GemInstaller::EnhancedStreamUI.new
|
64
|
+
@enhanced_stream_ui.outs = @outs_output_observer
|
65
|
+
@enhanced_stream_ui.errs = @errs_output_observer
|
66
|
+
@enhanced_stream_ui.gem_interaction_handler = @gem_interaction_handler
|
67
|
+
|
68
|
+
@gem_runner_class = Gem::GemRunner
|
69
|
+
@gem_cmd_manager_class = Gem::CommandManager
|
70
|
+
@exact_match_list_command = GemInstaller::ExactMatchListCommand.new
|
71
|
+
@gem_runner_proxy = GemInstaller::GemRunnerProxy.new
|
72
|
+
@gem_runner_proxy.gem_runner_class = @gem_runner_class
|
73
|
+
@gem_runner_proxy.gem_cmd_manager_class = @gem_cmd_manager_class
|
74
|
+
@gem_runner_proxy.output_listener = @output_listener
|
75
|
+
@gem_runner_proxy.enhanced_stream_ui = @enhanced_stream_ui
|
76
|
+
@gem_runner_proxy.options = @options
|
77
|
+
@gem_runner_proxy.output_filter = @output_filter
|
78
|
+
@gem_runner_proxy.exact_match_list_command = @exact_match_list_command
|
79
|
+
|
80
|
+
@gem_spec_manager = GemInstaller::GemSpecManager.new
|
81
|
+
@gem_spec_manager.valid_platform_selector = @valid_platform_selector
|
82
|
+
@gem_spec_manager.gem_source_index_proxy = @gem_source_index_proxy
|
83
|
+
@gem_spec_manager.output_filter = @output_filter
|
84
|
+
|
85
|
+
@gem_command_manager = GemInstaller::GemCommandManager.new
|
86
|
+
@gem_command_manager.gem_spec_manager = @gem_spec_manager
|
87
|
+
@gem_command_manager.gem_runner_proxy = @gem_runner_proxy
|
88
|
+
@gem_command_manager.gem_interaction_handler = @gem_interaction_handler
|
89
|
+
|
90
|
+
@rogue_gem_finder = GemInstaller::RogueGemFinder.new
|
91
|
+
@rogue_gem_finder.gem_command_manager = @gem_command_manager
|
92
|
+
@rogue_gem_finder.gem_spec_manager = @gem_spec_manager
|
93
|
+
@rogue_gem_finder.output_proxy = @output_proxy
|
94
|
+
|
95
|
+
@autogem = GemInstaller::Autogem.new
|
96
|
+
@autogem.gem_command_manager = @gem_command_manager
|
97
|
+
@autogem.gem_source_index_proxy = @gem_source_index_proxy
|
98
|
+
@autogem.gem_spec_manager = @gem_spec_manager
|
99
|
+
|
100
|
+
@gem_list_checker = GemInstaller::GemListChecker.new
|
101
|
+
@gem_list_checker.gem_command_manager = @gem_command_manager
|
102
|
+
@gem_list_checker.gem_arg_processor = @gem_arg_processor
|
103
|
+
@gem_list_checker.version_specifier = @version_specifier
|
104
|
+
|
105
|
+
@missing_dependency_finder = GemInstaller::MissingDependencyFinder.new
|
106
|
+
@missing_dependency_finder.gem_command_manager = @gem_command_manager
|
107
|
+
@missing_dependency_finder.gem_spec_manager = @gem_spec_manager
|
108
|
+
@missing_dependency_finder.gem_arg_processor = @gem_arg_processor
|
109
|
+
@missing_dependency_finder.output_filter = @output_filter
|
110
|
+
|
111
|
+
@install_processor = GemInstaller::InstallProcessor.new
|
112
|
+
@install_processor.options = @options
|
113
|
+
@install_processor.gem_list_checker = @gem_list_checker
|
114
|
+
@install_processor.gem_command_manager = @gem_command_manager
|
115
|
+
@install_processor.gem_spec_manager = @gem_spec_manager
|
116
|
+
@install_processor.missing_dependency_finder = @missing_dependency_finder
|
117
|
+
@install_processor.output_filter = @output_filter
|
118
|
+
|
119
|
+
@app = GemInstaller::Application.new
|
120
|
+
@app.autogem = @autogem
|
121
|
+
@app.rogue_gem_finder = @rogue_gem_finder
|
122
|
+
@app.options = @options
|
123
|
+
@app.arg_parser = @arg_parser
|
124
|
+
@app.config_builder = @config_builder
|
125
|
+
@app.install_processor = @install_processor
|
126
|
+
@app.output_filter = @output_filter
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class EnhancedStreamUI < Gem::StreamUI
|
3
|
+
attr_writer :gem_interaction_handler, :outs, :errs
|
4
|
+
|
5
|
+
def initialize()
|
6
|
+
# override default constructor to have no args
|
7
|
+
end
|
8
|
+
|
9
|
+
def ask_yes_no(question, default=nil)
|
10
|
+
begin
|
11
|
+
@gem_interaction_handler.handle_ask_yes_no(question)
|
12
|
+
rescue Exception => e
|
13
|
+
@outs.print(question)
|
14
|
+
@outs.flush
|
15
|
+
raise e
|
16
|
+
end
|
17
|
+
raise_unexpected_prompt_error(question)
|
18
|
+
end
|
19
|
+
|
20
|
+
def ask(question)
|
21
|
+
raise_unexpected_prompt_error(question)
|
22
|
+
end
|
23
|
+
|
24
|
+
def choose_from_list(question, list)
|
25
|
+
@gem_interaction_handler.handle_choose_from_list(question, list)
|
26
|
+
end
|
27
|
+
|
28
|
+
def terminate_interaction!(status=-1)
|
29
|
+
raise_error(status)
|
30
|
+
end
|
31
|
+
|
32
|
+
def terminate_interaction(status=0)
|
33
|
+
raise_error(status) unless status == 0
|
34
|
+
raise GemInstaller::RubyGemsExit.new(status)
|
35
|
+
end
|
36
|
+
|
37
|
+
def alert_error(statement, question=nil)
|
38
|
+
# if alert_error got called due to a GemInstaller::UnexpectedPromptError, re-throw it
|
39
|
+
last_exception = $!
|
40
|
+
if last_exception.class == GemInstaller::UnauthorizedDependencyPromptError || last_exception.class == GemInstaller::RubyGemsExit
|
41
|
+
raise last_exception
|
42
|
+
end
|
43
|
+
# otherwise let alert_error continue normally...
|
44
|
+
super(statement, question)
|
45
|
+
end
|
46
|
+
|
47
|
+
protected
|
48
|
+
def raise_error(status)
|
49
|
+
raise GemInstaller::GemInstallerError.new("RubyGems exited abnormally. Status: #{status}\n")
|
50
|
+
end
|
51
|
+
|
52
|
+
def raise_unexpected_prompt_error(question)
|
53
|
+
raise GemInstaller::UnexpectedPromptError.new("GemInstaller Internal Error - Unexpected prompt received from RubyGems: '#{question}'.")
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class ExactMatchListCommand < Gem::ListCommand
|
3
|
+
def execute
|
4
|
+
string = get_one_optional_argument || ''
|
5
|
+
# This overrides the default RubyGems ListCommand behavior of doing a wildcard match. This caused problems
|
6
|
+
# when some gems (ActiveRecord-JDBC) caused exceptions during a remote list, causing a remote list
|
7
|
+
# of other gems (activerecord) to fail as well
|
8
|
+
options[:name] = /^#{string}$/i
|
9
|
+
# Do a little metaprogramming magic to avoid calling the problematic execute method on the ListCommand
|
10
|
+
# superclass, and instead directly call the method on the QueryCommand grandparent 'supersuperclass'
|
11
|
+
unbound_execute_method = Gem::QueryCommand.instance_method(:execute)
|
12
|
+
bound_execute_method = unbound_execute_method.bind(self)
|
13
|
+
bound_execute_method.call
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -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,32 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class GemCommandLineProxy
|
3
|
+
GEM_ERROR_REGEXP = /^ERROR.*/
|
4
|
+
def run(args)
|
5
|
+
args_string = args.join(" ")
|
6
|
+
output = `gem #{args_string} 2>&1`
|
7
|
+
return_value = $?
|
8
|
+
lines = output.split("\n")
|
9
|
+
# return value is always zero on windows (from gem.bat), so we have to resort to parsing the output
|
10
|
+
error_in_output = false
|
11
|
+
lines.each do |line|
|
12
|
+
if line.match(GemInstaller::GemCommandLineProxy::GEM_ERROR_REGEXP)
|
13
|
+
error_in_output = true
|
14
|
+
break
|
15
|
+
end
|
16
|
+
end
|
17
|
+
if (return_value != 0 or error_in_output)
|
18
|
+
error_message = "Error: gem command failed. Command was 'gem #{args_string}', return value was '#{return_value}'. Output was:\n"
|
19
|
+
lines.each do |line|
|
20
|
+
error_message += ' ' + line + '\n'
|
21
|
+
end
|
22
|
+
raise GemInstaller::GemInstallerError.new(error_message)
|
23
|
+
end
|
24
|
+
return lines
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
|
31
|
+
|
32
|
+
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class GemCommandManager
|
3
|
+
attr_writer :gem_spec_manager, :gem_runner_proxy, :gem_interaction_handler
|
4
|
+
|
5
|
+
def list_remote_gem(gem, additional_options)
|
6
|
+
run_args = ["list",gem.name,"--remote"]
|
7
|
+
run_args += additional_options
|
8
|
+
@gem_runner_proxy.run(run_args)
|
9
|
+
end
|
10
|
+
|
11
|
+
def uninstall_gem(gem)
|
12
|
+
return if !@gem_spec_manager.is_gem_installed?(gem)
|
13
|
+
@gem_interaction_handler.dependent_gem = gem
|
14
|
+
run_gem_command('uninstall',gem)
|
15
|
+
end
|
16
|
+
|
17
|
+
def install_gem(gem)
|
18
|
+
return if @gem_spec_manager.is_gem_installed?(gem)
|
19
|
+
@gem_interaction_handler.dependent_gem = gem
|
20
|
+
run_gem_command('install',gem)
|
21
|
+
end
|
22
|
+
|
23
|
+
def dependency(name, version, additional_options = [])
|
24
|
+
# it would be great to use the dependency --pipe option, but unfortunately, rubygems has a bug
|
25
|
+
# up to at least 0.9.2 where the pipe options uses 'puts', instead of 'say', so we can't capture it
|
26
|
+
# with enhanced_stream_ui. Patch submitted:
|
27
|
+
# http://rubyforge.org/tracker/index.php?func=detail&aid=9020&group_id=126&atid=577
|
28
|
+
run_args = ["dependency",/^#{name}$/,"--version",version]
|
29
|
+
run_args += additional_options
|
30
|
+
output_lines = @gem_runner_proxy.run(run_args)
|
31
|
+
# dependency output has all lines in the first element
|
32
|
+
output_array = output_lines[0].split("\n")
|
33
|
+
# drop the first line which just echoes the dependent gem
|
34
|
+
output_array.shift
|
35
|
+
# drop the line containing 'requires' (rubygems < 0.9.0)
|
36
|
+
if output_array[0] == ' Requires'
|
37
|
+
output_array.shift
|
38
|
+
end
|
39
|
+
# drop all empty lines
|
40
|
+
output_array.reject! { |line| line == "" }
|
41
|
+
# strip leading space
|
42
|
+
output_array.each { |line| line.strip! }
|
43
|
+
# convert into gems
|
44
|
+
output_gems = output_array.collect do |line|
|
45
|
+
name = line.split(' ')[0]
|
46
|
+
version_spec = line.split(/[()]/)[1]
|
47
|
+
GemInstaller::RubyGem.new(name, :version => version_spec)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def run_gem_command(gem_command,gem)
|
52
|
+
run_args = [gem_command,gem.name,"--version", "#{gem.version}"]
|
53
|
+
run_args += gem.install_options
|
54
|
+
@gem_runner_proxy.run(run_args)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
|
62
|
+
|
@@ -0,0 +1,30 @@
|
|
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 handle_ask_yes_no(question)
|
7
|
+
return unless question.index(DEPENDENCY_PROMPT)
|
8
|
+
message = "Error: RubyGems is prompting to install a required dependency, and you have not " +
|
9
|
+
"specified the '--install-dependencies' option for the current gem. You must modify your " +
|
10
|
+
"geminstaller config file to either specify the '--install-depencencies' (-y) " +
|
11
|
+
"option, or explicitly add an entry for the dependency gem earlier in the file.\n"
|
12
|
+
raise GemInstaller::UnauthorizedDependencyPromptError.new(message)
|
13
|
+
end
|
14
|
+
|
15
|
+
def handle_choose_from_list(question, list, noninteractive_chooser = nil)
|
16
|
+
noninteractive_chooser ||= @noninteractive_chooser_class.new
|
17
|
+
valid_platforms = nil
|
18
|
+
if dependent_gem_with_platform_specified?(list, noninteractive_chooser) or noninteractive_chooser.uninstall_list_type?(question)
|
19
|
+
valid_platforms = [@dependent_gem.platform]
|
20
|
+
else
|
21
|
+
valid_platforms = @valid_platform_selector.select(@dependent_gem.platform)
|
22
|
+
end
|
23
|
+
noninteractive_chooser.choose(question, list, @dependent_gem.name, @dependent_gem.version, valid_platforms)
|
24
|
+
end
|
25
|
+
|
26
|
+
def dependent_gem_with_platform_specified?(list, noninteractive_chooser)
|
27
|
+
noninteractive_chooser.dependent_gem?(@dependent_gem.name, list) and @dependent_gem.platform
|
28
|
+
end
|
29
|
+
end
|
30
|
+
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}'. 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}', install options = '#{gem.install_options}'"
|
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,43 @@
|
|
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.register_command @exact_match_list_command
|
15
|
+
|
16
|
+
exit_status = nil
|
17
|
+
begin
|
18
|
+
gem_runner.run(args)
|
19
|
+
rescue GemInstaller::RubyGemsExit => normal_exit
|
20
|
+
exit_status = normal_exit.message
|
21
|
+
rescue GemInstaller::GemInstallerError => exit_error
|
22
|
+
raise_error_with_output(exit_error, args, @output_listener)
|
23
|
+
end
|
24
|
+
output_lines = @output_listener.read!
|
25
|
+
output_lines.push(exit_status) if exit_status
|
26
|
+
return output_lines
|
27
|
+
end
|
28
|
+
|
29
|
+
def create_gem_runner
|
30
|
+
rubygems_version = Gem::RubyGemsVersion
|
31
|
+
if rubygems_version.index('0.8') == 0
|
32
|
+
@gem_runner_class.new()
|
33
|
+
else
|
34
|
+
@gem_runner_class.new(:command_manager => @gem_cmd_manager_class)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def raise_error_with_output(exit_error, args, listener)
|
39
|
+
descriptive_exit_message = exit_error.descriptive_exit_message(exit_error.message, 'gem', args, listener)
|
40
|
+
raise exit_error.class.new(descriptive_exit_message)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
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
|
+
|