libgems 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (177) hide show
  1. data/ChangeLog +5811 -0
  2. data/History.txt +887 -0
  3. data/LICENSE.txt +51 -0
  4. data/README.md +87 -0
  5. data/Rakefile +113 -0
  6. data/lib/gauntlet_libgems.rb +50 -0
  7. data/lib/libgems.rb +1246 -0
  8. data/lib/libgems/builder.rb +102 -0
  9. data/lib/libgems/command.rb +534 -0
  10. data/lib/libgems/command_manager.rb +182 -0
  11. data/lib/libgems/commands/build_command.rb +53 -0
  12. data/lib/libgems/commands/cert_command.rb +86 -0
  13. data/lib/libgems/commands/check_command.rb +80 -0
  14. data/lib/libgems/commands/cleanup_command.rb +106 -0
  15. data/lib/libgems/commands/contents_command.rb +98 -0
  16. data/lib/libgems/commands/dependency_command.rb +195 -0
  17. data/lib/libgems/commands/environment_command.rb +133 -0
  18. data/lib/libgems/commands/fetch_command.rb +67 -0
  19. data/lib/libgems/commands/generate_index_command.rb +133 -0
  20. data/lib/libgems/commands/help_command.rb +172 -0
  21. data/lib/libgems/commands/install_command.rb +178 -0
  22. data/lib/libgems/commands/list_command.rb +35 -0
  23. data/lib/libgems/commands/lock_command.rb +110 -0
  24. data/lib/libgems/commands/mirror_command.rb +111 -0
  25. data/lib/libgems/commands/outdated_command.rb +33 -0
  26. data/lib/libgems/commands/owner_command.rb +75 -0
  27. data/lib/libgems/commands/pristine_command.rb +93 -0
  28. data/lib/libgems/commands/push_command.rb +56 -0
  29. data/lib/libgems/commands/query_command.rb +280 -0
  30. data/lib/libgems/commands/rdoc_command.rb +91 -0
  31. data/lib/libgems/commands/search_command.rb +31 -0
  32. data/lib/libgems/commands/server_command.rb +86 -0
  33. data/lib/libgems/commands/sources_command.rb +157 -0
  34. data/lib/libgems/commands/specification_command.rb +125 -0
  35. data/lib/libgems/commands/stale_command.rb +27 -0
  36. data/lib/libgems/commands/uninstall_command.rb +83 -0
  37. data/lib/libgems/commands/unpack_command.rb +121 -0
  38. data/lib/libgems/commands/update_command.rb +160 -0
  39. data/lib/libgems/commands/which_command.rb +86 -0
  40. data/lib/libgems/config_file.rb +345 -0
  41. data/lib/libgems/custom_require.rb +44 -0
  42. data/lib/libgems/defaults.rb +101 -0
  43. data/lib/libgems/dependency.rb +227 -0
  44. data/lib/libgems/dependency_installer.rb +286 -0
  45. data/lib/libgems/dependency_list.rb +208 -0
  46. data/lib/libgems/doc_manager.rb +242 -0
  47. data/lib/libgems/errors.rb +35 -0
  48. data/lib/libgems/exceptions.rb +91 -0
  49. data/lib/libgems/ext.rb +18 -0
  50. data/lib/libgems/ext/builder.rb +56 -0
  51. data/lib/libgems/ext/configure_builder.rb +25 -0
  52. data/lib/libgems/ext/ext_conf_builder.rb +24 -0
  53. data/lib/libgems/ext/rake_builder.rb +39 -0
  54. data/lib/libgems/format.rb +81 -0
  55. data/lib/libgems/gem_openssl.rb +92 -0
  56. data/lib/libgems/gem_path_searcher.rb +100 -0
  57. data/lib/libgems/gem_runner.rb +79 -0
  58. data/lib/libgems/gemcutter_utilities.rb +49 -0
  59. data/lib/libgems/indexer.rb +720 -0
  60. data/lib/libgems/install_update_options.rb +125 -0
  61. data/lib/libgems/installer.rb +604 -0
  62. data/lib/libgems/local_remote_options.rb +135 -0
  63. data/lib/libgems/old_format.rb +153 -0
  64. data/lib/libgems/package.rb +97 -0
  65. data/lib/libgems/package/f_sync_dir.rb +23 -0
  66. data/lib/libgems/package/tar_header.rb +266 -0
  67. data/lib/libgems/package/tar_input.rb +222 -0
  68. data/lib/libgems/package/tar_output.rb +144 -0
  69. data/lib/libgems/package/tar_reader.rb +106 -0
  70. data/lib/libgems/package/tar_reader/entry.rb +141 -0
  71. data/lib/libgems/package/tar_writer.rb +241 -0
  72. data/lib/libgems/package_task.rb +126 -0
  73. data/lib/libgems/platform.rb +183 -0
  74. data/lib/libgems/remote_fetcher.rb +414 -0
  75. data/lib/libgems/require_paths_builder.rb +18 -0
  76. data/lib/libgems/requirement.rb +153 -0
  77. data/lib/libgems/security.rb +814 -0
  78. data/lib/libgems/server.rb +872 -0
  79. data/lib/libgems/source_index.rb +597 -0
  80. data/lib/libgems/source_info_cache.rb +395 -0
  81. data/lib/libgems/source_info_cache_entry.rb +56 -0
  82. data/lib/libgems/spec_fetcher.rb +337 -0
  83. data/lib/libgems/specification.rb +1487 -0
  84. data/lib/libgems/test_utilities.rb +147 -0
  85. data/lib/libgems/text.rb +65 -0
  86. data/lib/libgems/uninstaller.rb +278 -0
  87. data/lib/libgems/user_interaction.rb +527 -0
  88. data/lib/libgems/validator.rb +240 -0
  89. data/lib/libgems/version.rb +316 -0
  90. data/lib/libgems/version_option.rb +65 -0
  91. data/lib/rbconfig/datadir.rb +20 -0
  92. data/test/bogussources.rb +8 -0
  93. data/test/data/gem-private_key.pem +27 -0
  94. data/test/data/gem-public_cert.pem +20 -0
  95. data/test/fake_certlib/openssl.rb +7 -0
  96. data/test/foo/discover.rb +0 -0
  97. data/test/gem_installer_test_case.rb +97 -0
  98. data/test/gem_package_tar_test_case.rb +132 -0
  99. data/test/gemutilities.rb +605 -0
  100. data/test/insure_session.rb +43 -0
  101. data/test/mockgemui.rb +56 -0
  102. data/test/plugin/exception/libgems_plugin.rb +2 -0
  103. data/test/plugin/load/libgems_plugin.rb +1 -0
  104. data/test/plugin/standarderror/libgems_plugin.rb +2 -0
  105. data/test/private_key.pem +27 -0
  106. data/test/public_cert.pem +20 -0
  107. data/test/rubygems_plugin.rb +21 -0
  108. data/test/simple_gem.rb +66 -0
  109. data/test/test_config.rb +12 -0
  110. data/test/test_gem.rb +780 -0
  111. data/test/test_gem_builder.rb +27 -0
  112. data/test/test_gem_command.rb +178 -0
  113. data/test/test_gem_command_manager.rb +207 -0
  114. data/test/test_gem_commands_build_command.rb +74 -0
  115. data/test/test_gem_commands_cert_command.rb +124 -0
  116. data/test/test_gem_commands_check_command.rb +18 -0
  117. data/test/test_gem_commands_contents_command.rb +156 -0
  118. data/test/test_gem_commands_dependency_command.rb +216 -0
  119. data/test/test_gem_commands_environment_command.rb +144 -0
  120. data/test/test_gem_commands_fetch_command.rb +76 -0
  121. data/test/test_gem_commands_generate_index_command.rb +135 -0
  122. data/test/test_gem_commands_install_command.rb +315 -0
  123. data/test/test_gem_commands_list_command.rb +36 -0
  124. data/test/test_gem_commands_lock_command.rb +68 -0
  125. data/test/test_gem_commands_mirror_command.rb +60 -0
  126. data/test/test_gem_commands_outdated_command.rb +40 -0
  127. data/test/test_gem_commands_owner_command.rb +105 -0
  128. data/test/test_gem_commands_pristine_command.rb +108 -0
  129. data/test/test_gem_commands_push_command.rb +81 -0
  130. data/test/test_gem_commands_query_command.rb +426 -0
  131. data/test/test_gem_commands_server_command.rb +59 -0
  132. data/test/test_gem_commands_sources_command.rb +209 -0
  133. data/test/test_gem_commands_specification_command.rb +139 -0
  134. data/test/test_gem_commands_stale_command.rb +38 -0
  135. data/test/test_gem_commands_uninstall_command.rb +83 -0
  136. data/test/test_gem_commands_unpack_command.rb +199 -0
  137. data/test/test_gem_commands_update_command.rb +207 -0
  138. data/test/test_gem_commands_which_command.rb +66 -0
  139. data/test/test_gem_config_file.rb +287 -0
  140. data/test/test_gem_dependency.rb +149 -0
  141. data/test/test_gem_dependency_installer.rb +661 -0
  142. data/test/test_gem_dependency_list.rb +230 -0
  143. data/test/test_gem_doc_manager.rb +31 -0
  144. data/test/test_gem_ext_configure_builder.rb +84 -0
  145. data/test/test_gem_ext_ext_conf_builder.rb +173 -0
  146. data/test/test_gem_ext_rake_builder.rb +81 -0
  147. data/test/test_gem_format.rb +70 -0
  148. data/test/test_gem_gem_path_searcher.rb +78 -0
  149. data/test/test_gem_gem_runner.rb +45 -0
  150. data/test/test_gem_gemcutter_utilities.rb +103 -0
  151. data/test/test_gem_indexer.rb +673 -0
  152. data/test/test_gem_install_update_options.rb +68 -0
  153. data/test/test_gem_installer.rb +857 -0
  154. data/test/test_gem_local_remote_options.rb +97 -0
  155. data/test/test_gem_package_tar_header.rb +130 -0
  156. data/test/test_gem_package_tar_input.rb +112 -0
  157. data/test/test_gem_package_tar_output.rb +97 -0
  158. data/test/test_gem_package_tar_reader.rb +46 -0
  159. data/test/test_gem_package_tar_reader_entry.rb +109 -0
  160. data/test/test_gem_package_tar_writer.rb +144 -0
  161. data/test/test_gem_package_task.rb +59 -0
  162. data/test/test_gem_platform.rb +264 -0
  163. data/test/test_gem_remote_fetcher.rb +740 -0
  164. data/test/test_gem_requirement.rb +292 -0
  165. data/test/test_gem_server.rb +356 -0
  166. data/test/test_gem_silent_ui.rb +113 -0
  167. data/test/test_gem_source_index.rb +461 -0
  168. data/test/test_gem_spec_fetcher.rb +410 -0
  169. data/test/test_gem_specification.rb +1334 -0
  170. data/test/test_gem_stream_ui.rb +218 -0
  171. data/test/test_gem_text.rb +43 -0
  172. data/test/test_gem_uninstaller.rb +146 -0
  173. data/test/test_gem_validator.rb +63 -0
  174. data/test/test_gem_version.rb +181 -0
  175. data/test/test_gem_version_option.rb +89 -0
  176. data/test/test_kernel.rb +59 -0
  177. metadata +402 -0
@@ -0,0 +1,79 @@
1
+ #--
2
+ # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+ require 'libgems/command_manager'
8
+ require 'libgems/config_file'
9
+ require 'libgems/doc_manager'
10
+
11
+ ##
12
+ # Run an instance of the gem program.
13
+ #
14
+ # LibGems::GemRunner is only intended for internal use by SlimGems itself. It
15
+ # does not form any public API and may change at any time for any reason.
16
+ #
17
+ # If you would like to duplicate functionality of `gem` commands, use the
18
+ # classes they call directly.
19
+
20
+ class LibGems::GemRunner
21
+
22
+ def initialize(options={})
23
+ @command_manager_class = options[:command_manager] || LibGems::CommandManager
24
+ @config_file_class = options[:config_file] || LibGems::ConfigFile
25
+ @doc_manager_class = options[:doc_manager] || LibGems::DocManager
26
+ end
27
+
28
+ ##
29
+ # Run the gem command with the following arguments.
30
+
31
+ def run(args)
32
+ start_time = Time.now
33
+
34
+ if args.include?('--')
35
+ # We need to preserve the original ARGV to use for passing gem options
36
+ # to source gems. If there is a -- in the line, strip all options after
37
+ # it...its for the source building process.
38
+ build_args = args[args.index("--") + 1...args.length]
39
+ args = args[0...args.index("--")]
40
+ end
41
+
42
+ LibGems::Command.build_args = build_args if build_args
43
+
44
+ do_configuration args
45
+ cmd = @command_manager_class.instance
46
+
47
+ cmd.command_names.each do |command_name|
48
+ config_args = LibGems.configuration[command_name]
49
+ config_args = case config_args
50
+ when String
51
+ config_args.split ' '
52
+ else
53
+ Array(config_args)
54
+ end
55
+ LibGems::Command.add_specific_extra_args command_name, config_args
56
+ end
57
+
58
+ cmd.run LibGems.configuration.args
59
+ end_time = Time.now
60
+
61
+ if LibGems.configuration.benchmark then
62
+ printf "\nExecution time: %0.2f seconds.\n", end_time - start_time
63
+ puts "Press Enter to finish"
64
+ STDIN.gets
65
+ end
66
+ end
67
+
68
+ private
69
+
70
+ def do_configuration(args)
71
+ LibGems.configuration = @config_file_class.new(args)
72
+ LibGems.use_paths(LibGems.configuration[:gemhome], LibGems.configuration[:gempath])
73
+ LibGems::Command.extra_args = LibGems.configuration[:gem]
74
+ @doc_manager_class.configured_args = LibGems.configuration[:rdoc]
75
+ end
76
+
77
+ end
78
+
79
+ LibGems.load_plugins
@@ -0,0 +1,49 @@
1
+ require 'libgems/remote_fetcher'
2
+
3
+ module LibGems::GemcutterUtilities
4
+
5
+ def sign_in
6
+ return if LibGems.configuration.rubygems_api_key
7
+
8
+ say "Enter your rubygems.org credentials."
9
+ say "Don't have an account yet? Create one at http://rubygems.org/sign_up"
10
+
11
+ email = ask " Email: "
12
+ password = ask_for_password "Password: "
13
+ say "\n"
14
+
15
+ response = rubygems_api_request :get, "api/v1/api_key" do |request|
16
+ request.basic_auth email, password
17
+ end
18
+
19
+ with_response response do |resp|
20
+ say "Signed in."
21
+ LibGems.configuration.rubygems_api_key = resp.body
22
+ end
23
+ end
24
+
25
+ def rubygems_api_request(method, path, host = LibGems.host, &block)
26
+ require 'net/http'
27
+ host = ENV['RUBYGEMS_HOST'] if ENV['RUBYGEMS_HOST']
28
+ uri = URI.parse "#{host}/#{path}"
29
+
30
+ request_method = Net::HTTP.const_get method.to_s.capitalize
31
+
32
+ LibGems::RemoteFetcher.fetcher.request(uri, request_method, &block)
33
+ end
34
+
35
+ def with_response(resp)
36
+ case resp
37
+ when Net::HTTPSuccess then
38
+ if block_given? then
39
+ yield resp
40
+ else
41
+ say resp.body
42
+ end
43
+ else
44
+ say resp.body
45
+ terminate_interaction 1
46
+ end
47
+ end
48
+
49
+ end
@@ -0,0 +1,720 @@
1
+ require 'fileutils'
2
+ require 'tmpdir'
3
+ require 'zlib'
4
+
5
+ require 'libgems'
6
+ require 'libgems/format'
7
+
8
+ begin
9
+ gem 'builder'
10
+ require 'builder/xchar'
11
+ rescue LoadError
12
+ end
13
+
14
+ ##
15
+ # Top level class for building the gem repository index.
16
+
17
+ class LibGems::Indexer
18
+
19
+ include LibGems::UserInteraction
20
+
21
+ ##
22
+ # Build indexes for SlimGems older than 1.2.0 when true
23
+
24
+ attr_accessor :build_legacy
25
+
26
+ ##
27
+ # Build indexes for SlimGems 1.2.0 and newer when true
28
+
29
+ attr_accessor :build_modern
30
+
31
+ ##
32
+ # Index install location
33
+
34
+ attr_reader :dest_directory
35
+
36
+ ##
37
+ # Specs index install location
38
+
39
+ attr_reader :dest_specs_index
40
+
41
+ ##
42
+ # Latest specs index install location
43
+
44
+ attr_reader :dest_latest_specs_index
45
+
46
+ ##
47
+ # Prerelease specs index install location
48
+
49
+ attr_reader :dest_prerelease_specs_index
50
+
51
+ ##
52
+ # Index build directory
53
+
54
+ attr_reader :directory
55
+
56
+ ##
57
+ # Create an indexer that will index the gems in +directory+.
58
+
59
+ def initialize(directory, options = {})
60
+ unless defined?(Builder::XChar) then
61
+ raise "LibGems::Indexer requires that the XML Builder library be installed:" \
62
+ "\n\tgem install builder"
63
+ end
64
+
65
+ options = { :build_legacy => true, :build_modern => true }.merge options
66
+
67
+ @build_legacy = options[:build_legacy]
68
+ @build_modern = options[:build_modern]
69
+
70
+ @rss_title = options[:rss_title]
71
+ @rss_host = options[:rss_host]
72
+ @rss_gems_host = options[:rss_gems_host]
73
+
74
+ @dest_directory = directory
75
+ @directory = File.join Dir.tmpdir, "gem_generate_index_#{$$}"
76
+
77
+ marshal_name = "Marshal.#{LibGems.marshal_version}"
78
+
79
+ @master_index = File.join @directory, 'yaml'
80
+ @marshal_index = File.join @directory, marshal_name
81
+
82
+ @quick_dir = File.join @directory, 'quick'
83
+
84
+ @quick_marshal_dir = File.join @quick_dir, marshal_name
85
+
86
+ @quick_index = File.join @quick_dir, 'index'
87
+ @latest_index = File.join @quick_dir, 'latest_index'
88
+
89
+ @specs_index = File.join @directory, "specs.#{LibGems.marshal_version}"
90
+ @latest_specs_index = File.join @directory,
91
+ "latest_specs.#{LibGems.marshal_version}"
92
+ @prerelease_specs_index = File.join(@directory,
93
+ "prerelease_specs.#{LibGems.marshal_version}")
94
+
95
+ @dest_specs_index = File.join @dest_directory,
96
+ "specs.#{LibGems.marshal_version}"
97
+ @dest_latest_specs_index = File.join @dest_directory,
98
+ "latest_specs.#{LibGems.marshal_version}"
99
+ @dest_prerelease_specs_index = File.join @dest_directory,
100
+ "prerelease_specs.#{LibGems.marshal_version}"
101
+
102
+ @rss_index = File.join @directory, 'index.rss'
103
+
104
+ @files = []
105
+ end
106
+
107
+ ##
108
+ # Abbreviate the spec for downloading. Abbreviated specs are only used for
109
+ # searching, downloading and related activities and do not need deployment
110
+ # specific information (e.g. list of files). So we abbreviate the spec,
111
+ # making it much smaller for quicker downloads.
112
+
113
+ def abbreviate(spec)
114
+ spec.files = []
115
+ spec.test_files = []
116
+ spec.rdoc_options = []
117
+ spec.extra_rdoc_files = []
118
+ spec.cert_chain = []
119
+ spec
120
+ end
121
+
122
+ ##
123
+ # Build various indicies
124
+
125
+ def build_indicies(index)
126
+ # Marshal gemspecs are used by both modern and legacy SlimGems
127
+ build_marshal_gemspecs index
128
+ build_legacy_indicies index if @build_legacy
129
+ build_modern_indicies index if @build_modern
130
+ build_rss index
131
+
132
+ compress_indicies
133
+ end
134
+
135
+ ##
136
+ # Builds indicies for SlimGems older than 1.2.x
137
+
138
+ def build_legacy_indicies(index)
139
+ progress = ui.progress_reporter index.size,
140
+ "Generating YAML quick index gemspecs for #{index.size} gems",
141
+ "Complete"
142
+
143
+ LibGems.time 'Generated YAML quick index gemspecs' do
144
+ index.released_gems.each do |original_name, spec|
145
+ spec_file_name = "#{original_name}.gemspec.rz"
146
+ yaml_name = File.join @quick_dir, spec_file_name
147
+
148
+ yaml_zipped = LibGems.deflate spec.to_yaml
149
+ open yaml_name, 'wb' do |io| io.write yaml_zipped end
150
+
151
+ progress.updated original_name
152
+ end
153
+
154
+ progress.done
155
+ end
156
+
157
+ say "Generating quick index"
158
+
159
+ LibGems.time 'Generated quick index' do
160
+ open @quick_index, 'wb' do |io|
161
+ io.puts index.sort.map { |_, spec| spec.original_name }
162
+ end
163
+ end
164
+
165
+ say "Generating latest index"
166
+
167
+ LibGems.time 'Generated latest index' do
168
+ open @latest_index, 'wb' do |io|
169
+ io.puts index.latest_specs.sort.map { |spec| spec.original_name }
170
+ end
171
+ end
172
+
173
+ # Don't need prerelease legacy index
174
+
175
+ say "Generating Marshal master index"
176
+
177
+ LibGems.time 'Generated Marshal master index' do
178
+ open @marshal_index, 'wb' do |io|
179
+ io.write index.dump
180
+ end
181
+ end
182
+
183
+ progress = ui.progress_reporter index.size,
184
+ "Generating YAML master index for #{index.size} gems (this may take a while)",
185
+ "Complete"
186
+
187
+ LibGems.time 'Generated YAML master index' do
188
+ open @master_index, 'wb' do |io|
189
+ io.puts "--- !ruby/object:#{index.class}"
190
+ io.puts "gems:"
191
+
192
+ gems = index.sort_by { |name, gemspec| gemspec.sort_obj }
193
+ gems.each do |original_name, gemspec|
194
+ yaml = gemspec.to_yaml.gsub(/^/, ' ')
195
+ yaml = yaml.sub(/\A ---/, '') # there's a needed extra ' ' here
196
+ io.print " #{original_name}:"
197
+ io.puts yaml
198
+
199
+ progress.updated original_name
200
+ end
201
+ end
202
+
203
+ progress.done
204
+ end
205
+
206
+ @files << @quick_dir
207
+ @files << @master_index
208
+ @files << "#{@master_index}.Z"
209
+ @files << @marshal_index
210
+ @files << "#{@marshal_index}.Z"
211
+ end
212
+
213
+ ##
214
+ # Builds Marshal quick index gemspecs.
215
+
216
+ def build_marshal_gemspecs(index)
217
+ progress = ui.progress_reporter index.size,
218
+ "Generating Marshal quick index gemspecs for #{index.size} gems",
219
+ "Complete"
220
+
221
+ files = []
222
+
223
+ LibGems.time 'Generated Marshal quick index gemspecs' do
224
+ index.gems.each do |original_name, spec|
225
+ spec_file_name = "#{original_name}.gemspec.rz"
226
+ marshal_name = File.join @quick_marshal_dir, spec_file_name
227
+
228
+ marshal_zipped = LibGems.deflate Marshal.dump(spec)
229
+ open marshal_name, 'wb' do |io| io.write marshal_zipped end
230
+
231
+ files << marshal_name
232
+
233
+ progress.updated original_name
234
+ end
235
+
236
+ progress.done
237
+ end
238
+
239
+ @files << @quick_marshal_dir
240
+
241
+ files
242
+ end
243
+
244
+ ##
245
+ # Build a single index for SlimGems 1.2 and newer
246
+
247
+ def build_modern_index(index, file, name)
248
+ say "Generating #{name} index"
249
+
250
+ LibGems.time "Generated #{name} index" do
251
+ open(file, 'wb') do |io|
252
+ specs = index.map do |*spec|
253
+ # We have to splat here because latest_specs is an array,
254
+ # while the others are hashes. See the TODO in source_index.rb
255
+ spec = spec.flatten.last
256
+ platform = spec.original_platform
257
+
258
+ # win32-api-1.0.4-x86-mswin32-60
259
+ unless String === platform then
260
+ alert_warning "Skipping invalid platform in gem: #{spec.full_name}"
261
+ next
262
+ end
263
+
264
+ platform = LibGems::Platform::RUBY if platform.nil? or platform.empty?
265
+ [spec.name, spec.version, platform]
266
+ end
267
+
268
+ specs = compact_specs(specs)
269
+ Marshal.dump(specs, io)
270
+ end
271
+ end
272
+ end
273
+
274
+ ##
275
+ # Builds indicies for SlimGems 1.2 and newer. Handles full, latest, prerelease
276
+
277
+ def build_modern_indicies(index)
278
+ build_modern_index(index.released_specs.sort, @specs_index, 'specs')
279
+ build_modern_index(index.latest_specs.sort,
280
+ @latest_specs_index,
281
+ 'latest specs')
282
+ build_modern_index(index.prerelease_specs.sort,
283
+ @prerelease_specs_index,
284
+ 'prerelease specs')
285
+
286
+ @files += [@specs_index,
287
+ "#{@specs_index}.gz",
288
+ @latest_specs_index,
289
+ "#{@latest_specs_index}.gz",
290
+ @prerelease_specs_index,
291
+ "#{@prerelease_specs_index}.gz"]
292
+ end
293
+
294
+ ##
295
+ # Builds an RSS feed for past two days gem releases according to the gem's
296
+ # date.
297
+
298
+ def build_rss(index)
299
+ if @rss_host.nil? or @rss_gems_host.nil? then
300
+ if LibGems.configuration.really_verbose then
301
+ alert_warning "no --rss-host or --rss-gems-host, RSS generation disabled"
302
+ end
303
+ return
304
+ end
305
+
306
+ require 'cgi'
307
+ require 'libgems/text'
308
+
309
+ extend LibGems::Text
310
+
311
+ LibGems.time 'Generated rss' do
312
+ open @rss_index, 'wb' do |io|
313
+ rss_host = CGI.escapeHTML @rss_host
314
+ rss_title = CGI.escapeHTML(@rss_title || 'gems')
315
+
316
+ io.puts <<-HEADER
317
+ <?xml version="1.0"?>
318
+ <rss version="2.0">
319
+ <channel>
320
+ <title>#{rss_title}</title>
321
+ <link>http://#{rss_host}</link>
322
+ <description>Recently released gems from http://#{rss_host}</description>
323
+ <generator>#{LibGems::NAME} v#{LibGems::VERSION} (RubyGems v#{LibGems::GEM_VERSION})</generator>
324
+ <docs>http://cyber.law.harvard.edu/rss/rss.html</docs>
325
+ HEADER
326
+
327
+ today = LibGems::Specification::TODAY
328
+ yesterday = today - 86400
329
+
330
+ index = index.select do |_, spec|
331
+ spec_date = spec.date
332
+
333
+ case spec_date
334
+ when Date
335
+ Time.parse(spec_date.to_s) >= yesterday
336
+ when Time
337
+ spec_date >= yesterday
338
+ end
339
+ end
340
+
341
+ index = index.select do |_, spec|
342
+ spec_date = spec.date
343
+
344
+ case spec_date
345
+ when Date
346
+ Time.parse(spec_date.to_s) <= today
347
+ when Time
348
+ spec_date <= today
349
+ end
350
+ end
351
+
352
+ index.sort_by { |_, spec| [-spec.date.to_i, spec] }.each do |_, spec|
353
+ gem_path = CGI.escapeHTML "http://#{@rss_gems_host}/gems/#{spec.file_name}"
354
+ size = File.stat(spec.loaded_from).size rescue next
355
+
356
+ description = spec.description || spec.summary || ''
357
+ authors = Array spec.authors
358
+ emails = Array spec.email
359
+ authors = emails.zip(authors).map do |email, author|
360
+ email += " (#{author})" if author and not author.empty?
361
+ end.join ', '
362
+
363
+ description = description.split(/\n\n+/).map do |chunk|
364
+ format_text chunk, 78
365
+ end
366
+
367
+ description = description.join "\n\n"
368
+
369
+ item = ''
370
+
371
+ item << <<-ITEM
372
+ <item>
373
+ <title>#{CGI.escapeHTML spec.full_name}</title>
374
+ <description>
375
+ &lt;pre&gt;#{CGI.escapeHTML description.chomp}&lt;/pre&gt;
376
+ </description>
377
+ <author>#{CGI.escapeHTML authors}</author>
378
+ <guid>#{CGI.escapeHTML spec.full_name}</guid>
379
+ <enclosure url=\"#{gem_path}\"
380
+ length=\"#{size}\" type=\"application/octet-stream\" />
381
+ <pubDate>#{spec.date.rfc2822}</pubDate>
382
+ ITEM
383
+
384
+ item << <<-ITEM if spec.homepage
385
+ <link>#{CGI.escapeHTML spec.homepage}</link>
386
+ ITEM
387
+
388
+ item << <<-ITEM
389
+ </item>
390
+ ITEM
391
+
392
+ io.puts item
393
+ end
394
+
395
+ io.puts <<-FOOTER
396
+ </channel>
397
+ </rss>
398
+ FOOTER
399
+ end
400
+ end
401
+
402
+ @files << @rss_index
403
+ end
404
+
405
+ ##
406
+ # Collect specifications from .gem files from the gem directory.
407
+
408
+ def collect_specs(gems = gem_file_list)
409
+ index = LibGems::SourceIndex.new
410
+
411
+ progress = ui.progress_reporter gems.size,
412
+ "Loading #{gems.size} gems from #{@dest_directory}",
413
+ "Loaded all gems"
414
+
415
+ LibGems.time 'loaded' do
416
+ gems.each do |gemfile|
417
+ if File.size(gemfile.to_s) == 0 then
418
+ alert_warning "Skipping zero-length gem: #{gemfile}"
419
+ next
420
+ end
421
+
422
+ begin
423
+ spec = LibGems::Format.from_file_by_path(gemfile).spec
424
+ spec.loaded_from = gemfile
425
+
426
+ unless gemfile =~ /\/#{Regexp.escape spec.original_name}.*\.gem\z/i then
427
+ expected_name = spec.full_name
428
+ expected_name << " (#{spec.original_name})" if
429
+ spec.original_name != spec.full_name
430
+ alert_warning "Skipping misnamed gem: #{gemfile} should be named #{expected_name}"
431
+ next
432
+ end
433
+
434
+ abbreviate spec
435
+ sanitize spec
436
+
437
+ index.add_spec spec, spec.original_name
438
+
439
+ progress.updated spec.original_name
440
+
441
+ rescue SignalException => e
442
+ alert_error "Received signal, exiting"
443
+ raise
444
+ rescue Exception => e
445
+ alert_error "Unable to process #{gemfile}\n#{e.message} (#{e.class})\n\t#{e.backtrace.join "\n\t"}"
446
+ end
447
+ end
448
+
449
+ progress.done
450
+ end
451
+
452
+ index
453
+ end
454
+
455
+ ##
456
+ # Compresses indicies on disk
457
+ #--
458
+ # All future files should be compressed using gzip, not deflate
459
+
460
+ def compress_indicies
461
+ say "Compressing indicies"
462
+
463
+ LibGems.time 'Compressed indicies' do
464
+ if @build_legacy then
465
+ compress @quick_index, 'rz'
466
+ paranoid @quick_index, 'rz'
467
+
468
+ compress @latest_index, 'rz'
469
+ paranoid @latest_index, 'rz'
470
+
471
+ compress @marshal_index, 'Z'
472
+ paranoid @marshal_index, 'Z'
473
+
474
+ compress @master_index, 'Z'
475
+ paranoid @master_index, 'Z'
476
+ end
477
+
478
+ if @build_modern then
479
+ gzip @specs_index
480
+ gzip @latest_specs_index
481
+ gzip @prerelease_specs_index
482
+ end
483
+ end
484
+ end
485
+
486
+ ##
487
+ # Compacts Marshal output for the specs index data source by using identical
488
+ # objects as much as possible.
489
+
490
+ def compact_specs(specs)
491
+ names = {}
492
+ versions = {}
493
+ platforms = {}
494
+
495
+ specs.map do |(name, version, platform)|
496
+ names[name] = name unless names.include? name
497
+ versions[version] = version unless versions.include? version
498
+ platforms[platform] = platform unless platforms.include? platform
499
+
500
+ [names[name], versions[version], platforms[platform]]
501
+ end
502
+ end
503
+
504
+ ##
505
+ # Compress +filename+ with +extension+.
506
+
507
+ def compress(filename, extension)
508
+ data = LibGems.read_binary filename
509
+
510
+ zipped = LibGems.deflate data
511
+
512
+ open "#{filename}.#{extension}", 'wb' do |io|
513
+ io.write zipped
514
+ end
515
+ end
516
+
517
+ ##
518
+ # List of gem file names to index.
519
+
520
+ def gem_file_list
521
+ Dir.glob(File.join(@dest_directory, "gems", "*.gem"))
522
+ end
523
+
524
+ ##
525
+ # Builds and installs indicies.
526
+
527
+ def generate_index
528
+ make_temp_directories
529
+ index = collect_specs
530
+ build_indicies index
531
+ install_indicies
532
+ rescue SignalException
533
+ ensure
534
+ FileUtils.rm_rf @directory
535
+ end
536
+
537
+ ##
538
+ # Zlib::GzipWriter wrapper that gzips +filename+ on disk.
539
+
540
+ def gzip(filename)
541
+ Zlib::GzipWriter.open "#{filename}.gz" do |io|
542
+ io.write LibGems.read_binary(filename)
543
+ end
544
+ end
545
+
546
+ ##
547
+ # Install generated indicies into the destination directory.
548
+
549
+ def install_indicies
550
+ verbose = LibGems.configuration.really_verbose
551
+
552
+ say "Moving index into production dir #{@dest_directory}" if verbose
553
+
554
+ files = @files.dup
555
+ files.delete @quick_marshal_dir if files.include? @quick_dir
556
+
557
+ if files.include? @quick_marshal_dir and
558
+ not files.include? @quick_dir then
559
+ files.delete @quick_marshal_dir
560
+ quick_marshal_dir = @quick_marshal_dir.sub @directory, ''
561
+
562
+ dst_name = File.join @dest_directory, quick_marshal_dir
563
+
564
+ FileUtils.mkdir_p File.dirname(dst_name), :verbose => verbose
565
+ FileUtils.rm_rf dst_name, :verbose => verbose
566
+ FileUtils.mv @quick_marshal_dir, dst_name, :verbose => verbose,
567
+ :force => true
568
+ end
569
+
570
+ files = files.map do |path|
571
+ path.sub @directory, ''
572
+ end
573
+
574
+ files.each do |file|
575
+ src_name = File.join @directory, file
576
+ dst_name = File.join @dest_directory, file
577
+
578
+ FileUtils.rm_rf dst_name, :verbose => verbose
579
+ FileUtils.mv src_name, @dest_directory, :verbose => verbose,
580
+ :force => true
581
+ end
582
+ end
583
+
584
+ ##
585
+ # Make directories for index generation
586
+
587
+ def make_temp_directories
588
+ FileUtils.rm_rf @directory
589
+ FileUtils.mkdir_p @directory, :mode => 0700
590
+ FileUtils.mkdir_p @quick_marshal_dir
591
+ end
592
+
593
+ ##
594
+ # Ensure +path+ and path with +extension+ are identical.
595
+
596
+ def paranoid(path, extension)
597
+ data = LibGems.read_binary path
598
+ compressed_data = LibGems.read_binary "#{path}.#{extension}"
599
+
600
+ unless data == LibGems.inflate(compressed_data) then
601
+ raise "Compressed file #{compressed_path} does not match uncompressed file #{path}"
602
+ end
603
+ end
604
+
605
+ ##
606
+ # Sanitize the descriptive fields in the spec. Sometimes non-ASCII
607
+ # characters will garble the site index. Non-ASCII characters will
608
+ # be replaced by their XML entity equivalent.
609
+
610
+ def sanitize(spec)
611
+ spec.summary = sanitize_string(spec.summary)
612
+ spec.description = sanitize_string(spec.description)
613
+ spec.post_install_message = sanitize_string(spec.post_install_message)
614
+ spec.authors = spec.authors.collect { |a| sanitize_string(a) }
615
+
616
+ spec
617
+ end
618
+
619
+ ##
620
+ # Sanitize a single string.
621
+
622
+ def sanitize_string(string)
623
+ return string unless string
624
+
625
+ # HACK the #to_s is in here because RSpec has an Array of Arrays of
626
+ # Strings for authors. Need a way to disallow bad values on gempsec
627
+ # generation. (Probably won't happen.)
628
+ string = string.to_s
629
+
630
+ begin
631
+ Builder::XChar.encode string
632
+ rescue NameError, NoMethodError
633
+ string.to_xs
634
+ end
635
+ end
636
+
637
+ ##
638
+ # Perform an in-place update of the repository from newly added gems. Only
639
+ # works for modern indicies, and sets #build_legacy to false when run.
640
+
641
+ def update_index
642
+ @build_legacy = false
643
+
644
+ make_temp_directories
645
+
646
+ specs_mtime = File.stat(@dest_specs_index).mtime
647
+ newest_mtime = Time.at 0
648
+
649
+ updated_gems = gem_file_list.select do |gem|
650
+ gem_mtime = File.stat(gem).mtime
651
+ newest_mtime = gem_mtime if gem_mtime > newest_mtime
652
+ gem_mtime >= specs_mtime
653
+ end
654
+
655
+ if updated_gems.empty? then
656
+ say 'No new gems'
657
+ terminate_interaction 0
658
+ end
659
+
660
+ index = collect_specs updated_gems
661
+
662
+ files = build_marshal_gemspecs index
663
+
664
+ LibGems.time 'Updated indexes' do
665
+ update_specs_index index.released_gems, @dest_specs_index, @specs_index
666
+ update_specs_index index.released_gems, @dest_latest_specs_index, @latest_specs_index
667
+ update_specs_index(index.prerelease_gems, @dest_prerelease_specs_index,
668
+ @prerelease_specs_index)
669
+ end
670
+
671
+ compress_indicies
672
+
673
+ verbose = LibGems.configuration.really_verbose
674
+
675
+ say "Updating production dir #{@dest_directory}" if verbose
676
+
677
+ files << @specs_index
678
+ files << "#{@specs_index}.gz"
679
+ files << @latest_specs_index
680
+ files << "#{@latest_specs_index}.gz"
681
+ files << @prerelease_specs_index
682
+ files << "#{@prerelease_specs_index}.gz"
683
+
684
+ files = files.map do |path|
685
+ path.sub @directory, ''
686
+ end
687
+
688
+ files.each do |file|
689
+ src_name = File.join @directory, file
690
+ dst_name = File.join @dest_directory, File.dirname(file)
691
+
692
+ FileUtils.mv src_name, dst_name, :verbose => verbose,
693
+ :force => true
694
+
695
+ File.utime newest_mtime, newest_mtime, dst_name
696
+ end
697
+ end
698
+
699
+ ##
700
+ # Combines specs in +index+ and +source+ then writes out a new copy to
701
+ # +dest+. For a latest index, does not ensure the new file is minimal.
702
+
703
+ def update_specs_index(index, source, dest)
704
+ specs_index = Marshal.load LibGems.read_binary(source)
705
+
706
+ index.each do |_, spec|
707
+ platform = spec.original_platform
708
+ platform = LibGems::Platform::RUBY if platform.nil? or platform.empty?
709
+ specs_index << [spec.name, spec.version, platform]
710
+ end
711
+
712
+ specs_index = compact_specs specs_index.uniq.sort
713
+
714
+ open dest, 'wb' do |io|
715
+ Marshal.dump specs_index, io
716
+ end
717
+ end
718
+
719
+ end
720
+