diff --git a/lib/motion/project/config.rb b/lib/motion/project/config.rb index 51e0f63b..f86fddd4 100644 --- a/lib/motion/project/config.rb +++ b/lib/motion/project/config.rb @@ -21,9 +21,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +require 'motion/project/app' + module Motion; module Project class Config - include Rake::DSL if Rake.const_defined?(:DSL) + include Rake::DSL if defined?(Rake) && Rake.const_defined?(:DSL) VARS = [] diff --git a/lib/motion/project/xcode_config.rb b/lib/motion/project/xcode_config.rb index ce39bd72..bd7b844b 100644 --- a/lib/motion/project/xcode_config.rb +++ b/lib/motion/project/xcode_config.rb @@ -21,6 +21,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +require 'motion/project/config' +require 'motion/util/code_sign' + module Motion; module Project; class XcodeConfig < Config variable :xcode_dir, :sdk_version, :deployment_target, :frameworks, @@ -47,7 +50,7 @@ module Motion; module Project; xcode_dot_app_path = '/Applications/Xcode.app/Contents/Developer' # First, honor /usr/bin/xcode-select - xcodeselect = '/usr/bin/xcode-select' + xcodeselect = '/usr/bin/xcode-select' if File.exist?(xcodeselect) path = `#{xcodeselect} -print-path`.strip if path.match(/^\/Developer\//) and File.exist?(xcode_dot_app_path) @@ -331,14 +334,15 @@ EOS def codesign_certificate(platform) @codesign_certificate ||= begin cert_type = (distribution_mode ? 'Distribution' : 'Developer') - certs = `/usr/bin/security -q find-certificate -a`.scan(/"#{platform} #{cert_type}: [^"]+"/).uniq + certs = Util::CodeSign.identity_names(release?).grep(/#{platform} #{cert_type}/) if certs.size == 0 App.fail "Cannot find any #{platform} #{cert_type} certificate in the keychain" elsif certs.size > 1 - App.warn "Found #{certs.size} #{platform} #{cert_type} certificates in the keychain. Set the `codesign_certificate' project setting. Will use the first certificate: `#{certs[0]}'" + # TODO list all the values for the user's convenience. + App.warn "Found #{certs.size} #{platform} #{cert_type} certificates in the keychain. Set the `codesign_certificate' project setting to explicitely use one of (defaults to the first): #{certs.join(', ')}" end - certs[0][1..-2] # trim trailing `"` characters - end + certs.first + end end def gen_bridge_metadata(platform, headers, bs_file, c_flags, exceptions=[]) diff --git a/lib/motion/util/code_sign.rb b/lib/motion/util/code_sign.rb new file mode 100644 index 00000000..a9253019 --- /dev/null +++ b/lib/motion/util/code_sign.rb @@ -0,0 +1,33 @@ +module Motion; module Util + module CodeSign + class << self + # @param Boolean valid_only Whether or not to include only valid code + # sign identities. + # + # @returns String The raw output from querying the `security` DB. + # + def query_security_db_for_identities(valid_only) + `/usr/bin/security -q find-identity -p codesigning#{' -v' if valid_only}`.strip + end + + # @param Boolean valid_only Whether or not to include only valid code + # sign identities. + # + # @returns Hash{String => String} The UUIDs and names of the identities. + # + def identities(valid_only) + output = query_security_db_for_identities(valid_only) + Hash[*output.scan(/([0-9A-F]{40})\s"(.+?)"/).flatten] + end + + # @param Boolean valid_only Whether or not to include only valid code + # sign identities. + # + # @returns Array The names of the identities. + # + def identity_names(valid_only) + identities(valid_only).values + end + end + end +end; end diff --git a/test/external/Gemfile b/test/external/Gemfile new file mode 100644 index 00000000..44250e12 --- /dev/null +++ b/test/external/Gemfile @@ -0,0 +1,5 @@ +source 'https://rubygems.org' + +gem 'bacon' +gem 'mocha' +gem 'mocha-on-bacon' diff --git a/test/external/Gemfile.lock b/test/external/Gemfile.lock new file mode 100644 index 00000000..5dddf2b0 --- /dev/null +++ b/test/external/Gemfile.lock @@ -0,0 +1,17 @@ +GEM + remote: https://rubygems.org/ + specs: + bacon (1.2.0) + metaclass (0.0.4) + mocha (1.0.0) + metaclass (~> 0.0.1) + mocha-on-bacon (0.2.2) + mocha (>= 0.13.0) + +PLATFORMS + ruby + +DEPENDENCIES + bacon + mocha + mocha-on-bacon diff --git a/test/external/Rakefile b/test/external/Rakefile index e59ff4ea..7aa16a7e 100644 --- a/test/external/Rakefile +++ b/test/external/Rakefile @@ -1,6 +1,6 @@ desc "Run all tests" task :spec do - sh "bacon **/*_spec.rb" + sh "bundle exec bacon #{FileList['**/*_spec.rb'].join(' ')}" end task :default => :spec diff --git a/test/external/spec_helper.rb b/test/external/spec_helper.rb new file mode 100644 index 00000000..2e7c8b4b --- /dev/null +++ b/test/external/spec_helper.rb @@ -0,0 +1,7 @@ +require 'bacon' +require 'mocha' +require 'mocha-on-bacon' + +$:.unshift File.expand_path('../../../lib', __FILE__) + +Mocha::Configuration.prevent(:stubbing_non_existent_method) diff --git a/test/external/unit/project/xcode_config_spec.rb b/test/external/unit/project/xcode_config_spec.rb new file mode 100644 index 00000000..40a4cb3c --- /dev/null +++ b/test/external/unit/project/xcode_config_spec.rb @@ -0,0 +1,49 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'motion/project/xcode_config' + +require 'tempfile' + +module Motion; module Project + + describe XcodeConfig do + describe "concerning codesign certificates" do + before do + Util::CodeSign.stubs(:identity_names).with(false).returns([ + 'iPhone Developer: Eloy Duran (K5B8YH2WD5)', + 'Mac Developer Self-Signed for Eloy Durán', + 'Mac Developer: Eloy Duran (K5B8YH2WD5)', + ]) + App.stubs(:warn) + @config = XcodeConfig.new(Dir.tmpdir, :development) + end + + it "selects an identity meant for codesigning iPhone apps" do + @config.codesign_certificate('iPhone').should == 'iPhone Developer: Eloy Duran (K5B8YH2WD5)' + end + + it "selects an identity meant for codesigning Mac apps" do + @config.codesign_certificate('Mac').should == 'Mac Developer Self-Signed for Eloy Durán' + end + + it "warns when there are multiple matching identities" do + App.expects(:warn) + @config.codesign_certificate('Mac') + end + + it "fails when there no identities could be found" do + App.expects(:fail) + @config.codesign_certificate('Android') + end + + it "limits identities to valid ones in release mode" do + Util::CodeSign.stubs(:identity_names).with(true).returns([ + 'iPhone Developer: Eloy Duran (K5B8YH2WD5)', + 'Mac Developer: Eloy Duran (K5B8YH2WD5)', + ]) + @config.instance_variable_set(:@build_mode, :release) + @config.codesign_certificate('Mac').should == 'Mac Developer: Eloy Duran (K5B8YH2WD5)' + end + end + end + +end; end diff --git a/test/external/unit/util/code_sign_spec.rb b/test/external/unit/util/code_sign_spec.rb new file mode 100644 index 00000000..6a54a77d --- /dev/null +++ b/test/external/unit/util/code_sign_spec.rb @@ -0,0 +1,91 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'motion/util/code_sign' + +module SpecHelper + module Fixtures + NO_CODESIGN_IDENTITIES = <<-EOS + +Policy: Code Signing + Matching identities + 0 identities found +EOS + + ALL_CODESIGN_IDENTITIES = <<-EOS + +Policy: Code Signing + Matching identities + 1) 5DD1D0DB197456F156D58D0F176200FB18840923 "Developer ID Application: Fingertips B.V." + 2) 689D97626D58D0F11F26AE048CBAD0FFB53545C9 "iPhone Developer: Eloy Duran (K5B8YH2WD5)" + 3) 01D976265D06F156D585560DB19740AD053C1162 "Mac Developer Self-Signed for Eloy Durán" (CSSMERR_TP_INVALID_ANCHOR_CERT) + 3 identities found + + Valid identities only + 1) 5DD1D0DB197456F156D58D0F176200FB18840923 "Developer ID Application: Fingertips B.V." + 2) 689D97626D58D0F11F26AE048CBAD0FFB53545C9 "iPhone Developer: Eloy Duran (K5B8YH2WD5)" + 2 valid identities found +EOS + + VALID_CODESIGN_IDENTITIES = <<-EOS + 1) 5DD1D0DB197456F156D58D0F176200FB18840923 "Developer ID Application: Fingertips B.V." + 2) 689D97626D58D0F11F26AE048CBAD0FFB53545C9 "iPhone Developer: Eloy Duran (K5B8YH2WD5)" + 2 valid identities found +EOS + end +end + +module Motion; module Util + + describe CodeSign do + it "queries the security database for all codesigning identities" do + CodeSign.expects(:`).with('/usr/bin/security -q find-identity -p codesigning').returns(' ALL ') + CodeSign.query_security_db_for_identities(false).should == 'ALL' + end + + it "queries the security database for valid codesigning identities" do + CodeSign.expects(:`).with('/usr/bin/security -q find-identity -p codesigning -v').returns(' VALID ') + CodeSign.query_security_db_for_identities(true).should == 'VALID' + end + + it "returns an empty list if there are no codesigning identities" do + CodeSign.stubs(:query_security_db_for_identities).returns(SpecHelper::Fixtures::NO_CODESIGN_IDENTITIES) + CodeSign.identities(false).should == {} + end + + before do + CodeSign.stubs(:query_security_db_for_identities).with(false).returns(SpecHelper::Fixtures::ALL_CODESIGN_IDENTITIES) + CodeSign.stubs(:query_security_db_for_identities).with(true).returns(SpecHelper::Fixtures::VALID_CODESIGN_IDENTITIES) + end + + it "returns all codesign identities" do + CodeSign.identities(false).should == { + '5DD1D0DB197456F156D58D0F176200FB18840923' => 'Developer ID Application: Fingertips B.V.', + '689D97626D58D0F11F26AE048CBAD0FFB53545C9' => 'iPhone Developer: Eloy Duran (K5B8YH2WD5)', + '01D976265D06F156D585560DB19740AD053C1162' => 'Mac Developer Self-Signed for Eloy Durán', + } + end + + it "returns only valid identities" do + CodeSign.identities(true).should == { + '5DD1D0DB197456F156D58D0F176200FB18840923' => 'Developer ID Application: Fingertips B.V.', + '689D97626D58D0F11F26AE048CBAD0FFB53545C9' => 'iPhone Developer: Eloy Duran (K5B8YH2WD5)', + } + end + + it "returns all codesign identity names" do + CodeSign.identity_names(false).should == [ + 'Developer ID Application: Fingertips B.V.', + 'iPhone Developer: Eloy Duran (K5B8YH2WD5)', + 'Mac Developer Self-Signed for Eloy Durán', + ] + end + + it "returns only valid identity names" do + CodeSign.identity_names(true).should == [ + 'Developer ID Application: Fingertips B.V.', + 'iPhone Developer: Eloy Duran (K5B8YH2WD5)', + ] + end + end + +end; end + diff --git a/test/external/unit/version_spec.rb b/test/external/unit/util/version_spec.rb similarity index 94% rename from test/external/unit/version_spec.rb rename to test/external/unit/util/version_spec.rb index 6c438f9f..d7b37fb3 100644 --- a/test/external/unit/version_spec.rb +++ b/test/external/unit/util/version_spec.rb @@ -1,6 +1,4 @@ -require 'bacon' - -$:.unshift File.expand_path('../../../../lib', __FILE__) +require File.expand_path('../../../spec_helper', __FILE__) require 'motion/util/version' module Motion; module Util diff --git a/test/test/vendor/code/code.h b/test/test/vendor/code/code.h index 9329ca55..7fee2030 100644 --- a/test/test/vendor/code/code.h +++ b/test/test/vendor/code/code.h @@ -107,3 +107,4 @@ ReturnsIntBlock KreateGlobalBlock(); - (BOOL)isEqualTo:(id)other; @end #endif + diff --git a/test/test/vendor/code/code.m b/test/test/vendor/code/code.m index 19d4c1b8..8145aab6 100644 --- a/test/test/vendor/code/code.m +++ b/test/test/vendor/code/code.m @@ -209,3 +209,4 @@ void _objc_insert_tagged_isa(unsigned char slotNumber, Class isa); @end #endif +