Patch for adding gem signing configuration and command line options #27

Closed
wants to merge 4 commits into
from
@@ -5,6 +5,23 @@ class Gem::Commands::BuildCommand < Gem::Command
def initialize
super('build', 'Build a gem from a gemspec')
+
+ add_option('-C', '--certificate-chain CERT',
+ 'Sets a certificate to the certificate chain, replacing the chain specified in the gemspec. CERT has to be a comma-separated list of cert files') do |value, options|
+ require 'rubygems/security'
+ options[:cert_chain] ||= []
+ value.split(',').each do |cert_file|
+ cert = OpenSSL::X509::Certificate.new(File.read(cert_file))
+ options[:cert_chain] << cert
+ end
+ end
+
+ add_option('-K', '--private-key KEY',
+ 'Set the private key for signing, replacing the one specified in gemspec') do |value, options|
+ require 'rubygems/security'
+ key = OpenSSL::PKey::RSA.new(File.read(value))
+ options[:signing_key] = key
+ end
end
def arguments # :nodoc:
@@ -20,6 +37,21 @@ def execute
if File.exist?(gemspec)
specs = load_gemspecs(gemspec)
specs.each do |spec|
+ # Set default cert_chain and signing_key if none are set in gemspec
+
+ # TODO: This may be better placed directly in the gemspec generation routine,
+ # but I did not want to break anything. -- Alexander E. Fischer <aef@raxys.net>, 2010-02-03
+ if not Gem.configuration.cert_chain.empty? and spec.cert_chain.empty?
+ spec.cert_chain = Gem.configuration.cert_chain
+ end
+
+ if not Gem.configuration.signing_key.nil? and spec.signing_key.nil?
+ spec.signing_key = Gem.configuration.signing_key
+ end
+
+ # Replaces cert_chain and signing_key if given on command line
+ spec.signing_key = options[:signing_key] if options[:signing_key]
+ spec.cert_chain = options[:cert_chain] if options[:cert_chain]
Gem::Builder.new(spec).build
end
else
@@ -68,18 +68,23 @@ def execute
source_uri = options[:add]
uri = URI.parse source_uri
- begin
- Gem::SpecFetcher.fetcher.load_specs uri, 'specs'
- Gem.sources << source_uri
- Gem.configuration.write
-
- say "#{source_uri} added to sources"
- rescue URI::Error, ArgumentError
- say "#{source_uri} is not a URI"
- terminate_interaction 1
- rescue Gem::RemoteFetcher::FetchError => e
- say "Error fetching #{source_uri}:\n\t#{e.message}"
- terminate_interaction 1
+ if Gem.sources.include?(source_uri)
+ say "#{source_uri} is already registered. Ignoring."
+ terminate_interaction 0
+ else
+ begin
+ Gem::SpecFetcher.fetcher.load_specs uri, 'specs'
+ Gem.sources << source_uri
+ Gem.configuration.write
+
+ say "#{source_uri} added to sources"
+ rescue URI::Error, ArgumentError
+ say "#{source_uri} is not a URI"
+ terminate_interaction 1
+ rescue Gem::RemoteFetcher::FetchError => e
+ say "Error fetching #{source_uri}:\n\t#{e.message}"
+ terminate_interaction 1
+ end
end
end
@@ -32,6 +32,8 @@ class Gem::ConfigFile
DEFAULT_BULK_THRESHOLD = 1000
DEFAULT_VERBOSITY = true
DEFAULT_UPDATE_SOURCES = true
+ DEFAULT_CERT_CHAIN = []
+ DEFAULT_SIGNING_KEY = nil
##
# For Ruby packagers to set configuration defaults. Set in
@@ -119,6 +121,17 @@ class Gem::ConfigFile
attr_accessor :update_sources
##
+ # Certificate chain for gem signing if none is specified in the gemspec
+ # This needs to be an array
+
+ attr_accessor :cert_chain
+
+ ##
+ # Key for gem signing if none is specified in the gemspec
+
+ attr_accessor :signing_key
+
+ ##
# API key for RubyGems.org
attr_reader :rubygems_api_key
@@ -167,6 +180,8 @@ def initialize(arg_list)
@bulk_threshold = DEFAULT_BULK_THRESHOLD
@verbose = DEFAULT_VERBOSITY
@update_sources = DEFAULT_UPDATE_SOURCES
+ @cert_chain = DEFAULT_CERT_CHAIN
+ @signing_key = DEFAULT_SIGNING_KEY
operating_system_config = Marshal.load Marshal.dump(OPERATING_SYSTEM_DEFAULTS)
platform_config = Marshal.load Marshal.dump(PLATFORM_DEFAULTS)
@@ -185,6 +200,8 @@ def initialize(arg_list)
@path = @hash[:gempath] if @hash.key? :gempath
@update_sources = @hash[:update_sources] if @hash.key? :update_sources
@verbose = @hash[:verbose] if @hash.key? :verbose
+ @cert_chain = @hash[:cert_chain] if @hash.key? :cert_chain
+ @signing_key = @hash[:signing_key] if @hash.key? :signing_key
load_rubygems_api_key
@@ -69,6 +69,47 @@ def test_execute_add
assert_equal '', @ui.error
end
+ def test_execute_add_duplicate
+ util_setup_fake_fetcher
+
+ si = Gem::SourceIndex.new
+ si.add_spec @a1
+
+ specs = si.map do |_, spec|
+ [spec.name, spec.version, spec.original_platform]
+ end
+
+ specs_dump_gz = StringIO.new
+ Zlib::GzipWriter.wrap specs_dump_gz do |io|
+ Marshal.dump specs, io
+ end
+
+ @fetcher.data["#{@new_repo}/specs.#{@marshal_version}.gz"] =
+ specs_dump_gz.string
+
+ @cmd.handle_options %W[--add #{@new_repo}]
+
+ util_setup_spec_fetcher
+
+ Gem.sources << @new_repo
+
+ use_ui @ui do
+ e = assert_raises Gem::SystemExitException do
+ @cmd.execute
+ end
+ assert_equal 0, e.exit_code, @ui.error
+ end
+
+ assert_equal [@gem_repo, @new_repo], Gem.sources
+
+ expected = <<-EOF
+#{@new_repo} is already registered. Ignoring.
+ EOF
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+ end
+
def test_execute_add_nonexistent_source
util_setup_fake_fetcher