geminstaller 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,42 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class GemSpecManager
|
3
|
+
attr_writer :gem_source_index_proxy, :valid_platform_selector, :output_filter
|
4
|
+
|
5
|
+
def search(gem_pattern, version_requirement)
|
6
|
+
@gem_source_index_proxy.refresh!
|
7
|
+
@gem_source_index_proxy.search(gem_pattern, version_requirement)
|
8
|
+
end
|
9
|
+
|
10
|
+
def is_gem_installed?(gem)
|
11
|
+
return local_matching_gems(gem).size > 0
|
12
|
+
end
|
13
|
+
|
14
|
+
def all_local_gems
|
15
|
+
all_local_specs = search('',GemInstaller::RubyGem.default_version)
|
16
|
+
return [] unless all_local_specs
|
17
|
+
all_local_gems = all_local_specs.collect do |spec|
|
18
|
+
gem = GemInstaller::RubyGem.new(spec.name, :version => spec.version.version)
|
19
|
+
end
|
20
|
+
return all_local_gems
|
21
|
+
end
|
22
|
+
|
23
|
+
def local_matching_gems(gem, exact_platform_match = true)
|
24
|
+
gem_name_regexp = /^#{gem.regexp_escaped_name}$/
|
25
|
+
found_gem_specs = search(gem_name_regexp,gem.version)
|
26
|
+
return [] unless found_gem_specs
|
27
|
+
matching_gem_specs = found_gem_specs.select do |gem_spec|
|
28
|
+
valid_platforms = @valid_platform_selector.select(gem.platform, exact_platform_match)
|
29
|
+
valid_platforms.include?(gem_spec.platform)
|
30
|
+
end
|
31
|
+
matching_gems = matching_gem_specs.map do |gem_spec|
|
32
|
+
GemInstaller::RubyGem.new(gem_spec.name, {:version => gem_spec.version.version, :platform => gem_spec.platform })
|
33
|
+
end
|
34
|
+
return matching_gems
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
|
41
|
+
|
42
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class GemInstallerError < RuntimeError
|
3
|
+
def descriptive_exit_message(message, command, args, listener)
|
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 += listener.read!.join("\n")
|
10
|
+
descriptive_exit_message += "\n=========================================================\n\n"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,56 @@
|
|
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
|
+
@gem_list_checker.verify_and_specify_remote_gem!(gem) unless already_specified
|
21
|
+
@output_filter.geminstaller_output(:install,"Invoking gem install for #{gem.name}, version #{gem.version}.\n")
|
22
|
+
output_lines = @gem_command_manager.install_gem(gem)
|
23
|
+
print_dependency_install_messages(gem, output_lines) unless @options[:silent]
|
24
|
+
end
|
25
|
+
if gem.fix_dependencies
|
26
|
+
fix_dependencies(gem)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def print_dependency_install_messages(gem, output_lines)
|
31
|
+
output_lines.each do |line|
|
32
|
+
line =~ /Successfully installed /
|
33
|
+
match = $'
|
34
|
+
next unless match
|
35
|
+
next if match =~ /#{gem.name}-/
|
36
|
+
@output_filter.geminstaller_output(:install,"Rubygems automatically installed dependency gem #{match}\n")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def fix_dependencies(gem)
|
41
|
+
missing_dependencies = @missing_dependency_finder.find(gem)
|
42
|
+
while (missing_dependencies.size > 0)
|
43
|
+
missing_dependencies.each do |missing_dependency|
|
44
|
+
@output_filter.geminstaller_output(:install,"Installing #{missing_dependency.name} (#{missing_dependency.version})\n")
|
45
|
+
# recursively call install_gem to install the missing dependency. Since fix_dependencies
|
46
|
+
# should never be set on an auto-created missing dependency gem, there is no risk of an
|
47
|
+
# endless loop or stack overflow.
|
48
|
+
install_gem(missing_dependency)
|
49
|
+
end
|
50
|
+
# continue to look for and install missing dependencies until none are found
|
51
|
+
missing_dependencies = @missing_dependency_finder.find(gem)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,41 @@
|
|
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
|
+
# NOTE: this doesn't resolve platforms, there's currently no way to know what
|
6
|
+
# platform should be selected for a dependency gem. Best-effort handling
|
7
|
+
# of ambiguous platforms on dependency gems will be handled elsewhere
|
8
|
+
matching_dependent_gems = @gem_spec_manager.local_matching_gems(dependent_gem)
|
9
|
+
missing_dependencies = []
|
10
|
+
install_options = dependent_gem.install_options
|
11
|
+
add_include_dependency_option(install_options)
|
12
|
+
common_args = @gem_arg_processor.strip_non_common_gem_args(install_options)
|
13
|
+
matching_dependent_gems.each do |matching_dependent_gem|
|
14
|
+
message_already_printed = false
|
15
|
+
dependency_gems = @gem_command_manager.dependency(matching_dependent_gem.name, matching_dependent_gem.version.to_s, common_args)
|
16
|
+
dependency_gems.each do |dependency_gem|
|
17
|
+
dependency_gem.install_options = install_options
|
18
|
+
local_matching_dependency_gems = @gem_spec_manager.local_matching_gems(dependency_gem, false)
|
19
|
+
unless local_matching_dependency_gems.size > 0
|
20
|
+
unless message_already_printed
|
21
|
+
@output_filter.geminstaller_output(:info, "Missing dependencies found for #{matching_dependent_gem.name} (#{matching_dependent_gem.version}):\n")
|
22
|
+
message_already_printed = true
|
23
|
+
end
|
24
|
+
# TODO: print install options too?
|
25
|
+
@output_filter.geminstaller_output(:info, " #{dependency_gem.name} (#{dependency_gem.version})\n")
|
26
|
+
missing_dependencies << dependency_gem
|
27
|
+
end
|
28
|
+
# recurse to find any missing dependencies in the tree
|
29
|
+
sub_dependencies = find(dependency_gem)
|
30
|
+
missing_dependencies += sub_dependencies if sub_dependencies.size > 0
|
31
|
+
end
|
32
|
+
end
|
33
|
+
return missing_dependencies
|
34
|
+
end
|
35
|
+
|
36
|
+
def add_include_dependency_option(install_options)
|
37
|
+
return if install_options.index('-y') or install_options.index('--include-dependencies')
|
38
|
+
install_options << '--include-dependencies'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,107 @@
|
|
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
|
+
@question = nil
|
7
|
+
end
|
8
|
+
|
9
|
+
def choose(question, list, dependent_gem_name, dependent_gem_version, valid_platforms)
|
10
|
+
@question = question
|
11
|
+
@list = list
|
12
|
+
@dependent_gem_name = dependent_gem_name
|
13
|
+
@dependent_gem_version = dependent_gem_version
|
14
|
+
@valid_platforms = valid_platforms
|
15
|
+
raise GemInstaller::GemInstallerError.new("Internal GemInstaller Error, unexpected choice prompt question: '#{question}'") unless
|
16
|
+
install_list_type? or uninstall_list_type?
|
17
|
+
|
18
|
+
raise GemInstaller::GemInstallerError.new("valid_platforms must be passed as an array.") unless
|
19
|
+
valid_platforms.respond_to? :shift
|
20
|
+
|
21
|
+
raise GemInstaller::GemInstallerError.new("Internal GemInstaller Error, multiple platforms cannot be specified for an uninstall prompt") if
|
22
|
+
uninstall_list_type? and valid_platforms.size > 1
|
23
|
+
|
24
|
+
index = nil
|
25
|
+
valid_platforms.each do |platform|
|
26
|
+
index = find_matching_line(platform)
|
27
|
+
break if index
|
28
|
+
end
|
29
|
+
|
30
|
+
return @list[index], index if index
|
31
|
+
|
32
|
+
# if we didn't find a match, raise a descriptive error
|
33
|
+
action = install_list_type? ? 'install' : 'uninstall'
|
34
|
+
name = @dependent_gem_name ? "#{@dependent_gem_name}" : "'any name'"
|
35
|
+
version = @dependent_gem_version ? "#{@dependent_gem_version}" : "'any version'"
|
36
|
+
platform = @valid_platforms ? " (#{@valid_platforms.join(' or ')})" : ''
|
37
|
+
error_message = "Error: Unable to select gem from list: \"#{name} #{version}#{platform}\". Available gems are:\n"
|
38
|
+
list.each do |item|
|
39
|
+
# skip last 'cancel' list entry, it's not a real gem
|
40
|
+
next if item == list.last
|
41
|
+
error_message += " " + item + "\n"
|
42
|
+
end
|
43
|
+
raise GemInstaller::GemInstallerError.new(error_message)
|
44
|
+
end
|
45
|
+
|
46
|
+
def find_matching_line(platform)
|
47
|
+
required_list_item_regexp = required_name_and_version_regexp + required_platform_regexp(platform)
|
48
|
+
line_selector = /^#{required_list_item_regexp}$/
|
49
|
+
@list.each_with_index do |item, index|
|
50
|
+
if item =~ line_selector
|
51
|
+
return index
|
52
|
+
end
|
53
|
+
end
|
54
|
+
return nil
|
55
|
+
end
|
56
|
+
|
57
|
+
def required_name_and_version_regexp
|
58
|
+
required_name_regexp = ".*?"
|
59
|
+
required_version_regexp = ".*?"
|
60
|
+
if uninstall_list_type? or dependent_gem?
|
61
|
+
# match the exact name and version if it's an uninstall prompt or a dependent gem
|
62
|
+
required_name_regexp = Regexp.escape(@dependent_gem_name) if @dependent_gem_name
|
63
|
+
required_version_regexp = Regexp.escape(@dependent_gem_version) if @dependent_gem_version
|
64
|
+
end
|
65
|
+
"#{required_name_regexp}[\s-]{0,1}#{required_version_regexp}"
|
66
|
+
end
|
67
|
+
|
68
|
+
def required_platform_regexp(platform_to_match)
|
69
|
+
return "" unless platform_to_match
|
70
|
+
required_platform = ""
|
71
|
+
escaped_platform_to_match = Regexp.escape(platform_to_match)
|
72
|
+
platform_regex = nil
|
73
|
+
if dependent_gem?
|
74
|
+
# do an exact match on the platform if this is the dependent gem
|
75
|
+
platform_regex = "#{escaped_platform_to_match}"
|
76
|
+
else
|
77
|
+
# do a wildcard match on the platform if it's not the dependent gem
|
78
|
+
platform_regex = ".*?#{escaped_platform_to_match}.*?"
|
79
|
+
end
|
80
|
+
# install list types always have the platform for each gem in parenthesis, even if it is ruby
|
81
|
+
if (install_list_type?)
|
82
|
+
required_platform = " \\(#{platform_regex}\\)"
|
83
|
+
end
|
84
|
+
# 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
|
85
|
+
if (uninstall_list_type? && platform_to_match.to_s != GemInstaller::RubyGem.default_platform)
|
86
|
+
required_platform = "-.*?#{platform_regex}.*?"
|
87
|
+
end
|
88
|
+
required_platform
|
89
|
+
end
|
90
|
+
|
91
|
+
def install_list_type?(question = @question)
|
92
|
+
question =~ /to install/
|
93
|
+
end
|
94
|
+
|
95
|
+
def uninstall_list_type?(question = @question)
|
96
|
+
question =~ /to uninstall/
|
97
|
+
end
|
98
|
+
|
99
|
+
def dependent_gem?(dependent_gem_name = @dependent_gem_name, list = @list)
|
100
|
+
# return true if it's an install prompt, and the list contains the gem for which the original install request
|
101
|
+
# was made (in other words, it's a dependent gem, not a dependency gem)
|
102
|
+
install_format_exact_name_match_regexp = /^#{dependent_gem_name}\s.*/
|
103
|
+
install_list_type? and list[0] =~ install_format_exact_name_match_regexp
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class OutputFilter
|
3
|
+
attr_writer :output_proxy, :options
|
4
|
+
|
5
|
+
def rubygems_output(type, message)
|
6
|
+
return if @options[:silent]
|
7
|
+
return unless rubygems_output_type_matches?(type)
|
8
|
+
output(:rubygems, type, message)
|
9
|
+
end
|
10
|
+
|
11
|
+
def geminstaller_output(type, message)
|
12
|
+
return if @options[:silent]
|
13
|
+
return unless geminstaller_output_type_matches?(type)
|
14
|
+
output(:geminstaller, type, message)
|
15
|
+
end
|
16
|
+
|
17
|
+
def output(source, type, message)
|
18
|
+
message = format_rubygems_message(type,message) if source == :rubygems
|
19
|
+
if (source == :geminstaller and type == :error)
|
20
|
+
@output_proxy.syserr(message)
|
21
|
+
|
22
|
+
else
|
23
|
+
@output_proxy.sysout(message)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def format_rubygems_message(type, message)
|
28
|
+
prefix = case type
|
29
|
+
when :stdout : "[RubyGems:stdout] "
|
30
|
+
when :stderr : "[RubyGems:stderr] "
|
31
|
+
end
|
32
|
+
"#{prefix}#{message}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def rubygems_output_type_matches?(type)
|
36
|
+
return false unless @options[:rubygems_output]
|
37
|
+
return true if @options[:rubygems_output].include?(:all)
|
38
|
+
return true if @options[:rubygems_output].include?(type)
|
39
|
+
return false
|
40
|
+
end
|
41
|
+
|
42
|
+
def geminstaller_output_type_matches?(type)
|
43
|
+
return false unless @options[:geminstaller_output]
|
44
|
+
return true if @options[:geminstaller_output].include?(:all)
|
45
|
+
return true if @options[:geminstaller_output].include?(type)
|
46
|
+
return false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
dir = File.dirname(__FILE__)
|
2
|
+
require File.expand_path("#{dir}/requires.rb")
|
3
|
+
|
4
|
+
module GemInstaller
|
5
|
+
class OutputListener
|
6
|
+
attr_writer :output_filter
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@messages = []
|
10
|
+
@output_filter = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def notify(msg, stream = :stdout)
|
14
|
+
@messages.push(msg)
|
15
|
+
return unless @output_filter
|
16
|
+
if stream == :stdout or stream == :stderr
|
17
|
+
@output_filter.rubygems_output(stream, msg)
|
18
|
+
else
|
19
|
+
raise GemInstaller::GemInstallerError.new("Invalid stream specified: #{@stream}")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def read
|
24
|
+
@messages.dup
|
25
|
+
end
|
26
|
+
|
27
|
+
def read!
|
28
|
+
messages = @messages.dup
|
29
|
+
@messages.clear
|
30
|
+
messages
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class OutputObserver
|
3
|
+
attr_writer :stream
|
4
|
+
# TODO: GemRunnerProxy should raise exception if listener still has any leftover output when run is invoked.
|
5
|
+
def initialize
|
6
|
+
@stream = :stdout
|
7
|
+
@listeners = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def register(listener)
|
11
|
+
listener = [listener] unless listener.is_a?(Array)
|
12
|
+
@listeners += listener
|
13
|
+
end
|
14
|
+
|
15
|
+
def unregister(listener)
|
16
|
+
@listeners.delete(listener)
|
17
|
+
end
|
18
|
+
|
19
|
+
def print(output)
|
20
|
+
@listeners.each do |listener|
|
21
|
+
listener.notify(output, @stream)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def puts(output)
|
26
|
+
print("#{output}\n")
|
27
|
+
end
|
28
|
+
|
29
|
+
def flush
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class OutputProxy
|
3
|
+
attr_writer :options
|
4
|
+
def default_stream=(stream)
|
5
|
+
raise GemInstaller::GemInstallerError.new("Invalid default stream: #{stream}") unless stream == :stderr or stream == :stdout
|
6
|
+
@default_stream = stream
|
7
|
+
end
|
8
|
+
|
9
|
+
def sysout(out)
|
10
|
+
return if silent?
|
11
|
+
$stdout.print out
|
12
|
+
end
|
13
|
+
|
14
|
+
def syserr(err)
|
15
|
+
return if silent?
|
16
|
+
if @options[:redirect_stderr_to_stdout]
|
17
|
+
$stdout.print err
|
18
|
+
else
|
19
|
+
$stderr.print err
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# TODO: should remove this, make callers explicitly choose.
|
24
|
+
def output(output)
|
25
|
+
if @default_stream == :stdout
|
26
|
+
sysout(output)
|
27
|
+
else
|
28
|
+
syserr(output)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def silent?
|
33
|
+
@options && @options[:silent]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
dir = File.dirname(__FILE__)
|
2
|
+
|
3
|
+
# requires for rubygems
|
4
|
+
require 'rubygems'
|
5
|
+
require 'rubygems/doc_manager'
|
6
|
+
require 'rubygems/config_file'
|
7
|
+
require 'rubygems/cmd_manager'
|
8
|
+
require 'rubygems/gem_runner'
|
9
|
+
require 'rubygems/remote_installer'
|
10
|
+
require 'rubygems/installer'
|
11
|
+
require 'rubygems/validator'
|
12
|
+
|
13
|
+
# third party libs
|
14
|
+
require 'erb'
|
15
|
+
require 'optparse'
|
16
|
+
require 'yaml'
|
17
|
+
require 'fileutils'
|
18
|
+
require 'tempfile'
|
19
|
+
|
20
|
+
# third party lib extensions
|
21
|
+
require File.expand_path("#{dir}/rubygems_extensions")
|
22
|
+
|
23
|
+
# internal files
|
24
|
+
require File.expand_path("#{dir}/../geminstaller")
|
25
|
+
require File.expand_path("#{dir}/application")
|
26
|
+
require File.expand_path("#{dir}/arg_parser")
|
27
|
+
require File.expand_path("#{dir}/autogem")
|
28
|
+
require File.expand_path("#{dir}/config")
|
29
|
+
require File.expand_path("#{dir}/config_builder")
|
30
|
+
require File.expand_path("#{dir}/dependency_injector")
|
31
|
+
require File.expand_path("#{dir}/enhanced_stream_ui")
|
32
|
+
require File.expand_path("#{dir}/exact_match_list_command")
|
33
|
+
require File.expand_path("#{dir}/file_reader")
|
34
|
+
require File.expand_path("#{dir}/gem_arg_processor")
|
35
|
+
require File.expand_path("#{dir}/gem_command_line_proxy")
|
36
|
+
require File.expand_path("#{dir}/gem_command_manager")
|
37
|
+
require File.expand_path("#{dir}/gem_interaction_handler")
|
38
|
+
require File.expand_path("#{dir}/gem_list_checker")
|
39
|
+
require File.expand_path("#{dir}/gem_runner_proxy")
|
40
|
+
require File.expand_path("#{dir}/gem_source_index_proxy")
|
41
|
+
require File.expand_path("#{dir}/gem_spec_manager")
|
42
|
+
require File.expand_path("#{dir}/geminstaller_error")
|
43
|
+
require File.expand_path("#{dir}/install_processor")
|
44
|
+
require File.expand_path("#{dir}/missing_dependency_finder")
|
45
|
+
require File.expand_path("#{dir}/missing_file_error")
|
46
|
+
require File.expand_path("#{dir}/noninteractive_chooser")
|
47
|
+
require File.expand_path("#{dir}/output_filter")
|
48
|
+
require File.expand_path("#{dir}/output_listener")
|
49
|
+
require File.expand_path("#{dir}/output_observer")
|
50
|
+
require File.expand_path("#{dir}/output_proxy")
|
51
|
+
require File.expand_path("#{dir}/rogue_gem_finder")
|
52
|
+
require File.expand_path("#{dir}/rubygems_exit")
|
53
|
+
require File.expand_path("#{dir}/ruby_gem")
|
54
|
+
require File.expand_path("#{dir}/unexpected_prompt_error")
|
55
|
+
require File.expand_path("#{dir}/unauthorized_dependency_prompt_error")
|
56
|
+
require File.expand_path("#{dir}/valid_platform_selector")
|
57
|
+
require File.expand_path("#{dir}/version_specifier")
|
58
|
+
require File.expand_path("#{dir}/yaml_loader")
|
59
|
+
|
@@ -0,0 +1,195 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class RogueGemFinder
|
3
|
+
attr_writer :output_proxy, :gem_command_manager, :gem_spec_manager, :boilerplate_lines, :preinstalled_gem_names, :preinstalled_comment
|
4
|
+
|
5
|
+
def print_rogue_gems(config_gems, config_file_paths)
|
6
|
+
@boilerplate_lines ||= default_boilerplate_lines(config_gems, config_file_paths)
|
7
|
+
@preinstalled_gem_names ||= default_preinstalled_gem_names
|
8
|
+
@preinstalled_comment ||= default_preinstalled_comment
|
9
|
+
@config_gems_with_dependencies = []
|
10
|
+
config_gems.each do |config_gem|
|
11
|
+
process_gem(config_gem)
|
12
|
+
end
|
13
|
+
all_local_gems = @gem_spec_manager.all_local_gems
|
14
|
+
rogue_gems = []
|
15
|
+
all_local_gems.each do |local_gem|
|
16
|
+
config_match_found_for_local = false
|
17
|
+
@config_gems_with_dependencies.each do |config_gem|
|
18
|
+
name_matches = config_gem.name == local_gem.name
|
19
|
+
config_gem_version_requirement = Gem::Version::Requirement.new [config_gem.version]
|
20
|
+
local_gem_version = Gem::Version.new(local_gem.version)
|
21
|
+
version_matches = config_gem_version_requirement.satisfied_by?(local_gem_version)
|
22
|
+
if (name_matches and version_matches)
|
23
|
+
config_match_found_for_local = true
|
24
|
+
break
|
25
|
+
end
|
26
|
+
end
|
27
|
+
rogue_gems << local_gem unless config_match_found_for_local
|
28
|
+
end
|
29
|
+
yaml = format_to_yaml(rogue_gems)
|
30
|
+
|
31
|
+
handle_output(yaml)
|
32
|
+
end
|
33
|
+
|
34
|
+
def default_boilerplate_lines(config_gems, config_file_paths)
|
35
|
+
boilerplate = []
|
36
|
+
boilerplate << [
|
37
|
+
"#",
|
38
|
+
"# This is a GemInstaller config file (http://geminstaller.rubyforge.org)",
|
39
|
+
"# It was generated using the 'geminstaller --print-rogue-gems' option on #{Time.now}",
|
40
|
+
"#",
|
41
|
+
"# You can bootstrap your config by piping the output to a file: ",
|
42
|
+
"# 'geminstaller --print-rogue-gems > geminstaller.yml'",
|
43
|
+
"#",
|
44
|
+
"# You can then install these gems on another system by running geminstaller with this file.",
|
45
|
+
"#"
|
46
|
+
]
|
47
|
+
|
48
|
+
if config_file_paths.size == 0
|
49
|
+
boilerplate << "# Since there was no config file specified, the 'gems:' section below lists all gems on your system."
|
50
|
+
else
|
51
|
+
boilerplate << "# The following config file(s) and gems were already specified:"
|
52
|
+
boilerplate << "# Files:"
|
53
|
+
config_file_paths.each do |config_file_path|
|
54
|
+
boilerplate << "# * " + config_file_path
|
55
|
+
end
|
56
|
+
boilerplate << "# Gems:"
|
57
|
+
if config_gems.size > 0
|
58
|
+
config_gems.each do |config_gem|
|
59
|
+
boilerplate << "# * #{config_gem.name} #{config_gem.version}"
|
60
|
+
end
|
61
|
+
else
|
62
|
+
boilerplate << "# No Gems"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
boilerplate << "#"
|
66
|
+
boilerplate << "defaults:"
|
67
|
+
boilerplate << " install_options: --include-dependencies"
|
68
|
+
return boilerplate
|
69
|
+
end
|
70
|
+
|
71
|
+
def default_preinstalled_gem_names
|
72
|
+
# This is the list of preinstalled gems from the Ruby windows 1-click installer, v186-25
|
73
|
+
#
|
74
|
+
# fxri (0.3.6)
|
75
|
+
# Graphical interface to the RI documentation, with search engine.
|
76
|
+
#
|
77
|
+
# fxruby (1.6.6)
|
78
|
+
# FXRuby is the Ruby binding to the FOX GUI toolkit.
|
79
|
+
#
|
80
|
+
# hpricot (0.4)
|
81
|
+
# a swift, liberal HTML parser with a fantastic library
|
82
|
+
#
|
83
|
+
# log4r (1.0.5)
|
84
|
+
# Log4r is a comprehensive and flexible logging library for Ruby.
|
85
|
+
#
|
86
|
+
# rake (0.7.2)
|
87
|
+
# Ruby based make-like utility.
|
88
|
+
#
|
89
|
+
# sources (0.0.1)
|
90
|
+
# This package provides download sources for remote gem installation
|
91
|
+
#
|
92
|
+
# win32-clipboard (0.4.1)
|
93
|
+
# A package for interacting with the Windows clipboard
|
94
|
+
#
|
95
|
+
# win32-dir (0.3.1)
|
96
|
+
# Extra constants and methods for the Dir class on Windows.
|
97
|
+
#
|
98
|
+
# win32-eventlog (0.4.3)
|
99
|
+
# Interface for the MS Windows Event Log.
|
100
|
+
#
|
101
|
+
# win32-file (0.5.3)
|
102
|
+
# Extra or redefined methods for the File class on Windows.
|
103
|
+
#
|
104
|
+
# win32-file-stat (1.2.3)
|
105
|
+
# A File::Stat class tailored to MS Windows
|
106
|
+
#
|
107
|
+
# win32-process (0.5.1)
|
108
|
+
# Adds fork, wait, wait2, waitpid, waitpid2 and a special kill method
|
109
|
+
#
|
110
|
+
# win32-sapi (0.1.3)
|
111
|
+
# An interface to the MS SAPI (Sound API) library.
|
112
|
+
#
|
113
|
+
# win32-sound (0.4.0)
|
114
|
+
# A package for playing with sound on Windows.
|
115
|
+
#
|
116
|
+
# windows-pr (0.6.2)
|
117
|
+
# Windows functions and constants predefined via Win32API
|
118
|
+
|
119
|
+
if RUBY_PLATFORM =~ /mswin/
|
120
|
+
return [
|
121
|
+
'fxri',
|
122
|
+
'fxruby',
|
123
|
+
'hpricot',
|
124
|
+
'log4r',
|
125
|
+
'rake',
|
126
|
+
'sources',
|
127
|
+
'win32-clipboard',
|
128
|
+
'win32-dir',
|
129
|
+
'win32-eventlog',
|
130
|
+
'win32-file',
|
131
|
+
'win32-file-stat',
|
132
|
+
'win32-process',
|
133
|
+
'win32-sapi',
|
134
|
+
'win32-sound',
|
135
|
+
'windows-pr'
|
136
|
+
]
|
137
|
+
end
|
138
|
+
return ['sources']
|
139
|
+
end
|
140
|
+
|
141
|
+
def default_preinstalled_comment
|
142
|
+
"# NOTE: This gem may have been automatially installed with Ruby, and not actually be used by your app(s)."
|
143
|
+
end
|
144
|
+
|
145
|
+
def handle_output(yaml)
|
146
|
+
yaml_lines = yaml.split("\n")
|
147
|
+
yaml_doc_separator = yaml_lines.delete_at(0)
|
148
|
+
|
149
|
+
output = []
|
150
|
+
output.push(yaml_doc_separator)
|
151
|
+
|
152
|
+
output << @boilerplate_lines
|
153
|
+
|
154
|
+
yaml_lines.each do |yaml_line|
|
155
|
+
name_parser_regexp = /- name: (.*)/
|
156
|
+
yaml_line =~ name_parser_regexp
|
157
|
+
gem_name = $1
|
158
|
+
preinstalled_comment = ''
|
159
|
+
if @preinstalled_gem_names.include?(gem_name)
|
160
|
+
preinstalled_comment = " " + @preinstalled_comment
|
161
|
+
end
|
162
|
+
output.push yaml_line + preinstalled_comment
|
163
|
+
end
|
164
|
+
|
165
|
+
output_string = output.join("\n")
|
166
|
+
@output_proxy.sysout output_string
|
167
|
+
output_string
|
168
|
+
end
|
169
|
+
|
170
|
+
def process_gem(gem)
|
171
|
+
@config_gems_with_dependencies << gem
|
172
|
+
process_gem_dependencies(gem)
|
173
|
+
end
|
174
|
+
|
175
|
+
def process_gem_dependencies(gem)
|
176
|
+
# TODO: this method is duplicated in autogem Should abstract and take a block
|
177
|
+
matching_gems = @gem_spec_manager.local_matching_gems(gem)
|
178
|
+
matching_gems.each do |matching_gem|
|
179
|
+
dependency_gems = @gem_command_manager.dependency(matching_gem.name, matching_gem.version.to_s)
|
180
|
+
dependency_gems.each do |dependency_gem|
|
181
|
+
process_gem(dependency_gem)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def format_to_yaml(gems)
|
187
|
+
names_and_versions = []
|
188
|
+
gems.each do |gem|
|
189
|
+
names_and_versions << {'name' => gem.name, 'version' => gem.version}
|
190
|
+
end
|
191
|
+
hash_for_yaml = {'gems' => names_and_versions}
|
192
|
+
YAML.dump(hash_for_yaml)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|