libgems 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +5811 -0
- data/History.txt +887 -0
- data/LICENSE.txt +51 -0
- data/README.md +87 -0
- data/Rakefile +113 -0
- data/lib/gauntlet_libgems.rb +50 -0
- data/lib/libgems.rb +1246 -0
- data/lib/libgems/builder.rb +102 -0
- data/lib/libgems/command.rb +534 -0
- data/lib/libgems/command_manager.rb +182 -0
- data/lib/libgems/commands/build_command.rb +53 -0
- data/lib/libgems/commands/cert_command.rb +86 -0
- data/lib/libgems/commands/check_command.rb +80 -0
- data/lib/libgems/commands/cleanup_command.rb +106 -0
- data/lib/libgems/commands/contents_command.rb +98 -0
- data/lib/libgems/commands/dependency_command.rb +195 -0
- data/lib/libgems/commands/environment_command.rb +133 -0
- data/lib/libgems/commands/fetch_command.rb +67 -0
- data/lib/libgems/commands/generate_index_command.rb +133 -0
- data/lib/libgems/commands/help_command.rb +172 -0
- data/lib/libgems/commands/install_command.rb +178 -0
- data/lib/libgems/commands/list_command.rb +35 -0
- data/lib/libgems/commands/lock_command.rb +110 -0
- data/lib/libgems/commands/mirror_command.rb +111 -0
- data/lib/libgems/commands/outdated_command.rb +33 -0
- data/lib/libgems/commands/owner_command.rb +75 -0
- data/lib/libgems/commands/pristine_command.rb +93 -0
- data/lib/libgems/commands/push_command.rb +56 -0
- data/lib/libgems/commands/query_command.rb +280 -0
- data/lib/libgems/commands/rdoc_command.rb +91 -0
- data/lib/libgems/commands/search_command.rb +31 -0
- data/lib/libgems/commands/server_command.rb +86 -0
- data/lib/libgems/commands/sources_command.rb +157 -0
- data/lib/libgems/commands/specification_command.rb +125 -0
- data/lib/libgems/commands/stale_command.rb +27 -0
- data/lib/libgems/commands/uninstall_command.rb +83 -0
- data/lib/libgems/commands/unpack_command.rb +121 -0
- data/lib/libgems/commands/update_command.rb +160 -0
- data/lib/libgems/commands/which_command.rb +86 -0
- data/lib/libgems/config_file.rb +345 -0
- data/lib/libgems/custom_require.rb +44 -0
- data/lib/libgems/defaults.rb +101 -0
- data/lib/libgems/dependency.rb +227 -0
- data/lib/libgems/dependency_installer.rb +286 -0
- data/lib/libgems/dependency_list.rb +208 -0
- data/lib/libgems/doc_manager.rb +242 -0
- data/lib/libgems/errors.rb +35 -0
- data/lib/libgems/exceptions.rb +91 -0
- data/lib/libgems/ext.rb +18 -0
- data/lib/libgems/ext/builder.rb +56 -0
- data/lib/libgems/ext/configure_builder.rb +25 -0
- data/lib/libgems/ext/ext_conf_builder.rb +24 -0
- data/lib/libgems/ext/rake_builder.rb +39 -0
- data/lib/libgems/format.rb +81 -0
- data/lib/libgems/gem_openssl.rb +92 -0
- data/lib/libgems/gem_path_searcher.rb +100 -0
- data/lib/libgems/gem_runner.rb +79 -0
- data/lib/libgems/gemcutter_utilities.rb +49 -0
- data/lib/libgems/indexer.rb +720 -0
- data/lib/libgems/install_update_options.rb +125 -0
- data/lib/libgems/installer.rb +604 -0
- data/lib/libgems/local_remote_options.rb +135 -0
- data/lib/libgems/old_format.rb +153 -0
- data/lib/libgems/package.rb +97 -0
- data/lib/libgems/package/f_sync_dir.rb +23 -0
- data/lib/libgems/package/tar_header.rb +266 -0
- data/lib/libgems/package/tar_input.rb +222 -0
- data/lib/libgems/package/tar_output.rb +144 -0
- data/lib/libgems/package/tar_reader.rb +106 -0
- data/lib/libgems/package/tar_reader/entry.rb +141 -0
- data/lib/libgems/package/tar_writer.rb +241 -0
- data/lib/libgems/package_task.rb +126 -0
- data/lib/libgems/platform.rb +183 -0
- data/lib/libgems/remote_fetcher.rb +414 -0
- data/lib/libgems/require_paths_builder.rb +18 -0
- data/lib/libgems/requirement.rb +153 -0
- data/lib/libgems/security.rb +814 -0
- data/lib/libgems/server.rb +872 -0
- data/lib/libgems/source_index.rb +597 -0
- data/lib/libgems/source_info_cache.rb +395 -0
- data/lib/libgems/source_info_cache_entry.rb +56 -0
- data/lib/libgems/spec_fetcher.rb +337 -0
- data/lib/libgems/specification.rb +1487 -0
- data/lib/libgems/test_utilities.rb +147 -0
- data/lib/libgems/text.rb +65 -0
- data/lib/libgems/uninstaller.rb +278 -0
- data/lib/libgems/user_interaction.rb +527 -0
- data/lib/libgems/validator.rb +240 -0
- data/lib/libgems/version.rb +316 -0
- data/lib/libgems/version_option.rb +65 -0
- data/lib/rbconfig/datadir.rb +20 -0
- data/test/bogussources.rb +8 -0
- data/test/data/gem-private_key.pem +27 -0
- data/test/data/gem-public_cert.pem +20 -0
- data/test/fake_certlib/openssl.rb +7 -0
- data/test/foo/discover.rb +0 -0
- data/test/gem_installer_test_case.rb +97 -0
- data/test/gem_package_tar_test_case.rb +132 -0
- data/test/gemutilities.rb +605 -0
- data/test/insure_session.rb +43 -0
- data/test/mockgemui.rb +56 -0
- data/test/plugin/exception/libgems_plugin.rb +2 -0
- data/test/plugin/load/libgems_plugin.rb +1 -0
- data/test/plugin/standarderror/libgems_plugin.rb +2 -0
- data/test/private_key.pem +27 -0
- data/test/public_cert.pem +20 -0
- data/test/rubygems_plugin.rb +21 -0
- data/test/simple_gem.rb +66 -0
- data/test/test_config.rb +12 -0
- data/test/test_gem.rb +780 -0
- data/test/test_gem_builder.rb +27 -0
- data/test/test_gem_command.rb +178 -0
- data/test/test_gem_command_manager.rb +207 -0
- data/test/test_gem_commands_build_command.rb +74 -0
- data/test/test_gem_commands_cert_command.rb +124 -0
- data/test/test_gem_commands_check_command.rb +18 -0
- data/test/test_gem_commands_contents_command.rb +156 -0
- data/test/test_gem_commands_dependency_command.rb +216 -0
- data/test/test_gem_commands_environment_command.rb +144 -0
- data/test/test_gem_commands_fetch_command.rb +76 -0
- data/test/test_gem_commands_generate_index_command.rb +135 -0
- data/test/test_gem_commands_install_command.rb +315 -0
- data/test/test_gem_commands_list_command.rb +36 -0
- data/test/test_gem_commands_lock_command.rb +68 -0
- data/test/test_gem_commands_mirror_command.rb +60 -0
- data/test/test_gem_commands_outdated_command.rb +40 -0
- data/test/test_gem_commands_owner_command.rb +105 -0
- data/test/test_gem_commands_pristine_command.rb +108 -0
- data/test/test_gem_commands_push_command.rb +81 -0
- data/test/test_gem_commands_query_command.rb +426 -0
- data/test/test_gem_commands_server_command.rb +59 -0
- data/test/test_gem_commands_sources_command.rb +209 -0
- data/test/test_gem_commands_specification_command.rb +139 -0
- data/test/test_gem_commands_stale_command.rb +38 -0
- data/test/test_gem_commands_uninstall_command.rb +83 -0
- data/test/test_gem_commands_unpack_command.rb +199 -0
- data/test/test_gem_commands_update_command.rb +207 -0
- data/test/test_gem_commands_which_command.rb +66 -0
- data/test/test_gem_config_file.rb +287 -0
- data/test/test_gem_dependency.rb +149 -0
- data/test/test_gem_dependency_installer.rb +661 -0
- data/test/test_gem_dependency_list.rb +230 -0
- data/test/test_gem_doc_manager.rb +31 -0
- data/test/test_gem_ext_configure_builder.rb +84 -0
- data/test/test_gem_ext_ext_conf_builder.rb +173 -0
- data/test/test_gem_ext_rake_builder.rb +81 -0
- data/test/test_gem_format.rb +70 -0
- data/test/test_gem_gem_path_searcher.rb +78 -0
- data/test/test_gem_gem_runner.rb +45 -0
- data/test/test_gem_gemcutter_utilities.rb +103 -0
- data/test/test_gem_indexer.rb +673 -0
- data/test/test_gem_install_update_options.rb +68 -0
- data/test/test_gem_installer.rb +857 -0
- data/test/test_gem_local_remote_options.rb +97 -0
- data/test/test_gem_package_tar_header.rb +130 -0
- data/test/test_gem_package_tar_input.rb +112 -0
- data/test/test_gem_package_tar_output.rb +97 -0
- data/test/test_gem_package_tar_reader.rb +46 -0
- data/test/test_gem_package_tar_reader_entry.rb +109 -0
- data/test/test_gem_package_tar_writer.rb +144 -0
- data/test/test_gem_package_task.rb +59 -0
- data/test/test_gem_platform.rb +264 -0
- data/test/test_gem_remote_fetcher.rb +740 -0
- data/test/test_gem_requirement.rb +292 -0
- data/test/test_gem_server.rb +356 -0
- data/test/test_gem_silent_ui.rb +113 -0
- data/test/test_gem_source_index.rb +461 -0
- data/test/test_gem_spec_fetcher.rb +410 -0
- data/test/test_gem_specification.rb +1334 -0
- data/test/test_gem_stream_ui.rb +218 -0
- data/test/test_gem_text.rb +43 -0
- data/test/test_gem_uninstaller.rb +146 -0
- data/test/test_gem_validator.rb +63 -0
- data/test/test_gem_version.rb +181 -0
- data/test/test_gem_version_option.rb +89 -0
- data/test/test_kernel.rb +59 -0
- 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
|
+
<pre>#{CGI.escapeHTML description.chomp}</pre>
|
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
|
+
|