motion-provisioning 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
 - data/.gitignore +10 -0
 - data/.rspec +1 -0
 - data/Gemfile +6 -0
 - data/Gemfile.lock +106 -0
 - data/LICENSE.txt +46 -0
 - data/README.md +207 -0
 - data/Rakefile +71 -0
 - data/bin/console +6 -0
 - data/bin/setup +8 -0
 - data/lib/motion-provisioning.rb +231 -0
 - data/lib/motion-provisioning/application.rb +53 -0
 - data/lib/motion-provisioning/certificate.rb +278 -0
 - data/lib/motion-provisioning/mobileprovision.rb +84 -0
 - data/lib/motion-provisioning/provisioning_profile.rb +136 -0
 - data/lib/motion-provisioning/spaceship/free_portal_client.rb +123 -0
 - data/lib/motion-provisioning/spaceship/portal_client.rb +34 -0
 - data/lib/motion-provisioning/tasks.rb +14 -0
 - data/lib/motion-provisioning/utils.rb +59 -0
 - data/lib/motion-provisioning/version.rb +3 -0
 - data/motion-provisioning.gemspec +29 -0
 - metadata +192 -0
 
    
        data/bin/console
    ADDED
    
    
    
        data/bin/setup
    ADDED
    
    
| 
         @@ -0,0 +1,231 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'fileutils'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'yaml'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'base64'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'date'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            require 'plist'
         
     | 
| 
      
 7 
     | 
    
         
            +
            require 'security'
         
     | 
| 
      
 8 
     | 
    
         
            +
            require 'spaceship'
         
     | 
| 
      
 9 
     | 
    
         
            +
            require 'motion-provisioning/spaceship/portal_client'
         
     | 
| 
      
 10 
     | 
    
         
            +
            require 'motion-provisioning/spaceship/free_portal_client'
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            require 'motion-provisioning/utils'
         
     | 
| 
      
 13 
     | 
    
         
            +
            require 'motion-provisioning/tasks'
         
     | 
| 
      
 14 
     | 
    
         
            +
            require 'motion-provisioning/version'
         
     | 
| 
      
 15 
     | 
    
         
            +
            require 'motion-provisioning/service'
         
     | 
| 
      
 16 
     | 
    
         
            +
            require 'motion-provisioning/certificate'
         
     | 
| 
      
 17 
     | 
    
         
            +
            require 'motion-provisioning/application'
         
     | 
| 
      
 18 
     | 
    
         
            +
            require 'motion-provisioning/mobileprovision'
         
     | 
| 
      
 19 
     | 
    
         
            +
            require 'motion-provisioning/provisioning_profile'
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            module MotionProvisioning
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
              class << self
         
     | 
| 
      
 24 
     | 
    
         
            +
                attr_accessor :free, :team
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
              def self.client
         
     | 
| 
      
 28 
     | 
    
         
            +
                Spaceship::Portal.client ||= begin
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  if File.exist?('.gitignore') && File.read('.gitignore').match(/^provisioning$/).nil?
         
     | 
| 
      
 31 
     | 
    
         
            +
                    answer = Utils.ask("Info", "Do you want to add the 'provisioning' folder fo your '.gitignore' file? (Recommended) (Y/n):")
         
     | 
| 
      
 32 
     | 
    
         
            +
                    `echo provisioning >> .gitignore` if answer.downcase == 'y'
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                  client = if free
         
     | 
| 
      
 36 
     | 
    
         
            +
                    Spaceship::FreePortalClient.new
         
     | 
| 
      
 37 
     | 
    
         
            +
                  else
         
     | 
| 
      
 38 
     | 
    
         
            +
                    Spaceship::PortalClient.new
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                  email = ENV['MOTION_PROVISIONING_EMAIL'] || MotionProvisioning.config['email'] || Utils.ask("Info", "Your Apple ID email:")
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  config_path = File.expand_path('./provisioning/config.yaml')
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                  if ENV['MOTION_PROVISIONING_EMAIL'].nil? && !File.exist?(config_path)
         
     | 
| 
      
 46 
     | 
    
         
            +
                    answer = Utils.ask("Info", "Do you want to save the email to the config file ('provisioning/config.yaml') so you dont have to type it again? (Y/n):")
         
     | 
| 
      
 47 
     | 
    
         
            +
                    if answer.downcase == 'y'
         
     | 
| 
      
 48 
     | 
    
         
            +
                      FileUtils.mkdir_p(File.expand_path('./provisioning'))
         
     | 
| 
      
 49 
     | 
    
         
            +
                      File.write(config_path, { 'email' => email }.to_yaml)
         
     | 
| 
      
 50 
     | 
    
         
            +
                    end
         
     | 
| 
      
 51 
     | 
    
         
            +
                  end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                  password = ENV['MOTION_PROVISIONING_PASSWORD']
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                  server_name = "motionprovisioning.#{email}"
         
     | 
| 
      
 56 
     | 
    
         
            +
                  item = Security::InternetPassword.find(server: server_name)
         
     | 
| 
      
 57 
     | 
    
         
            +
                  password ||= item.password if item
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                  if password.nil?
         
     | 
| 
      
 60 
     | 
    
         
            +
                    puts "The login information you enter will be stored safely in the macOS keychain."
         
     | 
| 
      
 61 
     | 
    
         
            +
                    password = Utils.ask_password("Info", "Password for #{email}:")
         
     | 
| 
      
 62 
     | 
    
         
            +
                    Security::InternetPassword.add(server_name, email, password)
         
     | 
| 
      
 63 
     | 
    
         
            +
                  end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                  Utils.log("Info", "Logging into the Developer Portal with email '#{email}'.")
         
     | 
| 
      
 66 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 67 
     | 
    
         
            +
                    client.user = email
         
     | 
| 
      
 68 
     | 
    
         
            +
                    client.send("do_login", email, password)
         
     | 
| 
      
 69 
     | 
    
         
            +
                  rescue Spaceship::Client::InvalidUserCredentialsError => ex
         
     | 
| 
      
 70 
     | 
    
         
            +
                    Utils.log("Error", "There was an error logging into your account. Your password may be wrong.")
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                    if Utils.ask("Info", 'Do you want to reenter your password? (Y/n):').downcase == 'y'
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                      # The 'delete' method is very verbose, temporarily disable output
         
     | 
| 
      
 75 
     | 
    
         
            +
                      orig_stdout = $stdout.dup
         
     | 
| 
      
 76 
     | 
    
         
            +
                      $stdout.reopen('/dev/null', 'w')
         
     | 
| 
      
 77 
     | 
    
         
            +
                      Security::InternetPassword.delete(server: server_name)
         
     | 
| 
      
 78 
     | 
    
         
            +
                      $stdout.reopen(orig_stdout)
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                      password = Utils.ask_password("Info", "Password for #{email}:")
         
     | 
| 
      
 81 
     | 
    
         
            +
                      Security::InternetPassword.add(server_name, email, password)
         
     | 
| 
      
 82 
     | 
    
         
            +
                      retry
         
     | 
| 
      
 83 
     | 
    
         
            +
                    else
         
     | 
| 
      
 84 
     | 
    
         
            +
                      abort
         
     | 
| 
      
 85 
     | 
    
         
            +
                    end
         
     | 
| 
      
 86 
     | 
    
         
            +
                  end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                  if self.free
         
     | 
| 
      
 89 
     | 
    
         
            +
                    client.teams.each do |team|
         
     | 
| 
      
 90 
     | 
    
         
            +
                      if team['currentTeamMember']['roles'].include?('XCODE_FREE_USER')
         
     | 
| 
      
 91 
     | 
    
         
            +
                        client.team_id = team['teamId']
         
     | 
| 
      
 92 
     | 
    
         
            +
                        self.team = team
         
     | 
| 
      
 93 
     | 
    
         
            +
                      end
         
     | 
| 
      
 94 
     | 
    
         
            +
                    end
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
                    if client.team_id.nil?
         
     | 
| 
      
 97 
     | 
    
         
            +
                      raise "could not find free team"
         
     | 
| 
      
 98 
     | 
    
         
            +
                    end
         
     | 
| 
      
 99 
     | 
    
         
            +
                  else
         
     | 
| 
      
 100 
     | 
    
         
            +
                    if team_id = MotionProvisioning.config['team_id'] || ENV['MOTION_PROVISIONING_TEAM_ID']
         
     | 
| 
      
 101 
     | 
    
         
            +
                      found = false
         
     | 
| 
      
 102 
     | 
    
         
            +
                      client.teams.each do |team|
         
     | 
| 
      
 103 
     | 
    
         
            +
                        if team_id == team['teamId']
         
     | 
| 
      
 104 
     | 
    
         
            +
                          client.team_id = team_id
         
     | 
| 
      
 105 
     | 
    
         
            +
                          self.team = team
         
     | 
| 
      
 106 
     | 
    
         
            +
                          found = true
         
     | 
| 
      
 107 
     | 
    
         
            +
                        end
         
     | 
| 
      
 108 
     | 
    
         
            +
                      end
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                      if found == false
         
     | 
| 
      
 111 
     | 
    
         
            +
                        raise "The current user does not belong to team with ID '#{team_id}' selected in config.yml."
         
     | 
| 
      
 112 
     | 
    
         
            +
                      end
         
     | 
| 
      
 113 
     | 
    
         
            +
                    else
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
                      team_id = client.select_team
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
      
 117 
     | 
    
         
            +
                      if File.exist?(config_path) && ENV['MOTION_PROVISIONING_TEAM_ID'].nil?
         
     | 
| 
      
 118 
     | 
    
         
            +
                        answer = Utils.ask("Info", "Do you want to save the team id (#{team_id}) in the config file ('provisioning/config.yaml') so you dont have to select it again? (Y/n):")
         
     | 
| 
      
 119 
     | 
    
         
            +
                        if answer.downcase == 'y'
         
     | 
| 
      
 120 
     | 
    
         
            +
                          config = YAML.load(File.read(config_path))
         
     | 
| 
      
 121 
     | 
    
         
            +
                          config['team_id'] = team_id
         
     | 
| 
      
 122 
     | 
    
         
            +
                          File.write(config_path, config.to_yaml)
         
     | 
| 
      
 123 
     | 
    
         
            +
                        end
         
     | 
| 
      
 124 
     | 
    
         
            +
                      end
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
                      self.team = client.teams.detect { |team| team['teamId'] == team_id }
         
     | 
| 
      
 127 
     | 
    
         
            +
                    end
         
     | 
| 
      
 128 
     | 
    
         
            +
                  end
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
                  Utils.log("Info", "Selected team '#{self.team['name']} (#{self.team['teamId']})'.")
         
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
                  Spaceship::App.set_client(client)
         
     | 
| 
      
 133 
     | 
    
         
            +
                  Spaceship::AppGroup.set_client(client)
         
     | 
| 
      
 134 
     | 
    
         
            +
                  Spaceship::Device.set_client(client)
         
     | 
| 
      
 135 
     | 
    
         
            +
                  Spaceship::Certificate.set_client(client)
         
     | 
| 
      
 136 
     | 
    
         
            +
                  Spaceship::ProvisioningProfile.set_client(client)
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
                  client
         
     | 
| 
      
 139 
     | 
    
         
            +
                end
         
     | 
| 
      
 140 
     | 
    
         
            +
              end
         
     | 
| 
      
 141 
     | 
    
         
            +
             
     | 
| 
      
 142 
     | 
    
         
            +
              def self.config
         
     | 
| 
      
 143 
     | 
    
         
            +
                return @config if @config
         
     | 
| 
      
 144 
     | 
    
         
            +
                config_path = File.expand_path('./provisioning/config.yaml')
         
     | 
| 
      
 145 
     | 
    
         
            +
                if File.exist?(config_path)
         
     | 
| 
      
 146 
     | 
    
         
            +
                  @config = YAML.load(File.read(config_path)) || {}
         
     | 
| 
      
 147 
     | 
    
         
            +
                else
         
     | 
| 
      
 148 
     | 
    
         
            +
                  @config = {}
         
     | 
| 
      
 149 
     | 
    
         
            +
                end
         
     | 
| 
      
 150 
     | 
    
         
            +
              end
         
     | 
| 
      
 151 
     | 
    
         
            +
             
     | 
| 
      
 152 
     | 
    
         
            +
              def self.services
         
     | 
| 
      
 153 
     | 
    
         
            +
                @services ||= []
         
     | 
| 
      
 154 
     | 
    
         
            +
              end
         
     | 
| 
      
 155 
     | 
    
         
            +
             
     | 
| 
      
 156 
     | 
    
         
            +
              def self.services=(services)
         
     | 
| 
      
 157 
     | 
    
         
            +
                @services = services
         
     | 
| 
      
 158 
     | 
    
         
            +
              end
         
     | 
| 
      
 159 
     | 
    
         
            +
             
     | 
| 
      
 160 
     | 
    
         
            +
              def self.entitlements
         
     | 
| 
      
 161 
     | 
    
         
            +
                self.services.map(&:to_hash).inject({}, &:merge!)
         
     | 
| 
      
 162 
     | 
    
         
            +
              end
         
     | 
| 
      
 163 
     | 
    
         
            +
             
     | 
| 
      
 164 
     | 
    
         
            +
              def self.certificate(opts = {})
         
     | 
| 
      
 165 
     | 
    
         
            +
                unless opts[:platform]
         
     | 
| 
      
 166 
     | 
    
         
            +
                  Utils.log("Error", "Certificate 'platform' is required")
         
     | 
| 
      
 167 
     | 
    
         
            +
                  exit(1)
         
     | 
| 
      
 168 
     | 
    
         
            +
                end
         
     | 
| 
      
 169 
     | 
    
         
            +
             
     | 
| 
      
 170 
     | 
    
         
            +
                unless opts[:type]
         
     | 
| 
      
 171 
     | 
    
         
            +
                  Utils.log("Error", "Certificate 'type' is required")
         
     | 
| 
      
 172 
     | 
    
         
            +
                  exit(1)
         
     | 
| 
      
 173 
     | 
    
         
            +
                end
         
     | 
| 
      
 174 
     | 
    
         
            +
             
     | 
| 
      
 175 
     | 
    
         
            +
                if opts[:free] == true && opts[:type] != :development
         
     | 
| 
      
 176 
     | 
    
         
            +
                  Utils.log("Error", "You can only create a 'free' certificate for type 'development'. You selected type '#{opts[:type].to_s}'")
         
     | 
| 
      
 177 
     | 
    
         
            +
                  exit(1)
         
     | 
| 
      
 178 
     | 
    
         
            +
                end
         
     | 
| 
      
 179 
     | 
    
         
            +
             
     | 
| 
      
 180 
     | 
    
         
            +
                opts[:platform] = :ios if opts[:platform] == :tvos
         
     | 
| 
      
 181 
     | 
    
         
            +
             
     | 
| 
      
 182 
     | 
    
         
            +
                supported_platforms = [:ios, :mac]
         
     | 
| 
      
 183 
     | 
    
         
            +
                unless supported_platforms.include?(opts[:platform])
         
     | 
| 
      
 184 
     | 
    
         
            +
                  Utils.log("Error", "Invalid value'#{opts[:platform]}'for 'platorm'. Supported values: #{supported_platforms}")
         
     | 
| 
      
 185 
     | 
    
         
            +
                  exit(1)
         
     | 
| 
      
 186 
     | 
    
         
            +
                end
         
     | 
| 
      
 187 
     | 
    
         
            +
             
     | 
| 
      
 188 
     | 
    
         
            +
                supported_types = [:distribution, :development, :developer_id]
         
     | 
| 
      
 189 
     | 
    
         
            +
                unless supported_types.include?(opts[:type])
         
     | 
| 
      
 190 
     | 
    
         
            +
                  Utils.log("Error", "Invalid value '#{opts[:type]}'for 'type'. Supported values: #{supported_types}")
         
     | 
| 
      
 191 
     | 
    
         
            +
                  exit(1)
         
     | 
| 
      
 192 
     | 
    
         
            +
                end
         
     | 
| 
      
 193 
     | 
    
         
            +
             
     | 
| 
      
 194 
     | 
    
         
            +
                MotionProvisioning.free = opts[:free]
         
     | 
| 
      
 195 
     | 
    
         
            +
                Certificate.new.certificate_name(opts[:type], opts[:platform])
         
     | 
| 
      
 196 
     | 
    
         
            +
              end
         
     | 
| 
      
 197 
     | 
    
         
            +
             
     | 
| 
      
 198 
     | 
    
         
            +
             
     | 
| 
      
 199 
     | 
    
         
            +
              def self.profile(opts = {})
         
     | 
| 
      
 200 
     | 
    
         
            +
                unless opts[:bundle_identifier]
         
     | 
| 
      
 201 
     | 
    
         
            +
                  Utils.log("Error", "'bundle_identifier' is required")
         
     | 
| 
      
 202 
     | 
    
         
            +
                  exit(1)
         
     | 
| 
      
 203 
     | 
    
         
            +
                end
         
     | 
| 
      
 204 
     | 
    
         
            +
             
     | 
| 
      
 205 
     | 
    
         
            +
                unless opts[:app_name]
         
     | 
| 
      
 206 
     | 
    
         
            +
                  Utils.log("Error", "'app_name' is required")
         
     | 
| 
      
 207 
     | 
    
         
            +
                  exit(1)
         
     | 
| 
      
 208 
     | 
    
         
            +
                end
         
     | 
| 
      
 209 
     | 
    
         
            +
             
     | 
| 
      
 210 
     | 
    
         
            +
                supported_platforms = [:ios, :tvos, :mac]
         
     | 
| 
      
 211 
     | 
    
         
            +
                unless supported_platforms.include?(opts[:platform])
         
     | 
| 
      
 212 
     | 
    
         
            +
                  Utils.log("Error", "Invalid value'#{opts[:platform]}'for 'platorm'. Supported values: #{supported_platforms}")
         
     | 
| 
      
 213 
     | 
    
         
            +
                  exit(1)
         
     | 
| 
      
 214 
     | 
    
         
            +
                end
         
     | 
| 
      
 215 
     | 
    
         
            +
             
     | 
| 
      
 216 
     | 
    
         
            +
                supported_types = [:distribution, :adhoc, :development]
         
     | 
| 
      
 217 
     | 
    
         
            +
                unless supported_types.include?(opts[:type])
         
     | 
| 
      
 218 
     | 
    
         
            +
                  Utils.log("Error", "Invalid value '#{opts[:type]}'for 'type'. Supported values: #{supported_types}")
         
     | 
| 
      
 219 
     | 
    
         
            +
                  exit(1)
         
     | 
| 
      
 220 
     | 
    
         
            +
                end
         
     | 
| 
      
 221 
     | 
    
         
            +
             
     | 
| 
      
 222 
     | 
    
         
            +
                if opts[:free] == true && opts[:type] != :development
         
     | 
| 
      
 223 
     | 
    
         
            +
                  Utils.log("Error", "You can only create a 'free' provisioning profile for type 'development'. You selected type '#{opts[:type].to_s}'")
         
     | 
| 
      
 224 
     | 
    
         
            +
                  exit(1)
         
     | 
| 
      
 225 
     | 
    
         
            +
                end
         
     | 
| 
      
 226 
     | 
    
         
            +
             
     | 
| 
      
 227 
     | 
    
         
            +
                MotionProvisioning.free = opts[:free]
         
     | 
| 
      
 228 
     | 
    
         
            +
                ProvisioningProfile.new.provisioning_profile(opts[:bundle_identifier], opts[:app_name], opts[:platform], opts[:type])
         
     | 
| 
      
 229 
     | 
    
         
            +
              end
         
     | 
| 
      
 230 
     | 
    
         
            +
             
     | 
| 
      
 231 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,53 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module MotionProvisioning
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Application
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
                # Finds or create app for the given bundle id and name
         
     | 
| 
      
 5 
     | 
    
         
            +
                def self.find_or_create(bundle_id: nil, name: nil, mac: mac = false)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  app = Spaceship::Portal::App.find(bundle_id, mac: mac)
         
     | 
| 
      
 7 
     | 
    
         
            +
                  if app
         
     | 
| 
      
 8 
     | 
    
         
            +
                    app = app.details if app.features.nil?
         
     | 
| 
      
 9 
     | 
    
         
            +
                  else
         
     | 
| 
      
 10 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 11 
     | 
    
         
            +
                      app = Spaceship::Portal::App.create!(bundle_id: bundle_id, name: name, mac: mac)
         
     | 
| 
      
 12 
     | 
    
         
            +
                      app = app.details if app.features.nil?
         
     | 
| 
      
 13 
     | 
    
         
            +
                    rescue Spaceship::Client::UnexpectedResponse => e
         
     | 
| 
      
 14 
     | 
    
         
            +
                      if e.to_s.include?("is not a valid identifier")
         
     | 
| 
      
 15 
     | 
    
         
            +
                        Utils.log("Error", "'#{bundle_id}' is not a valid identifier for an app. Please choose an identifier containing only alphanumeric characters, dots and asterisk")
         
     | 
| 
      
 16 
     | 
    
         
            +
                        exit 1
         
     | 
| 
      
 17 
     | 
    
         
            +
                      elsif e.to_s.include?("is not available")
         
     | 
| 
      
 18 
     | 
    
         
            +
                        Utils.log("Error", "'#{bundle_id}' has already been taken. Please enter a different string.")
         
     | 
| 
      
 19 
     | 
    
         
            +
                        exit 1
         
     | 
| 
      
 20 
     | 
    
         
            +
                      else
         
     | 
| 
      
 21 
     | 
    
         
            +
                        raise e
         
     | 
| 
      
 22 
     | 
    
         
            +
                      end
         
     | 
| 
      
 23 
     | 
    
         
            +
                    end
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                  # services = MotionProvisioning.services
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  # Disable all app services not enabled via entitlements
         
     | 
| 
      
 29 
     | 
    
         
            +
                  # app.enabled_features.each do |feature_id|
         
     | 
| 
      
 30 
     | 
    
         
            +
                  #   # These services are always enabled and cannot be disabled
         
     | 
| 
      
 31 
     | 
    
         
            +
                  #   next if ['inAppPurchase', 'gameCenter', 'push'].include?(feature_id)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  #   service = services.detect { |s| s.identifier == feature_id }
         
     | 
| 
      
 33 
     | 
    
         
            +
                  #   if service.nil?
         
     | 
| 
      
 34 
     | 
    
         
            +
                  #     Utils.log('Info', "Disabling unused app service '#{feature_id}' for '#{bundle_id}'")
         
     | 
| 
      
 35 
     | 
    
         
            +
                  #     # To disable Data Protection we need to send an empty string as value
         
     | 
| 
      
 36 
     | 
    
         
            +
                  #     value = feature_id == 'dataProtection' ? '' : false
         
     | 
| 
      
 37 
     | 
    
         
            +
                  #     app.update_service(Spaceship::Portal::AppService.new(feature_id, value))
         
     | 
| 
      
 38 
     | 
    
         
            +
                  #   end
         
     | 
| 
      
 39 
     | 
    
         
            +
                  # end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                  # # Enable all app services enabled via entitlements (or which have a different value)
         
     | 
| 
      
 42 
     | 
    
         
            +
                  # services.each do |service|
         
     | 
| 
      
 43 
     | 
    
         
            +
                  #   value = service.identifier == 'dataProtection' ? 'complete' : true
         
     | 
| 
      
 44 
     | 
    
         
            +
                  #   if app.features[service.identifier] != value
         
     | 
| 
      
 45 
     | 
    
         
            +
                  #     Utils.log('Info', "Enabling app service '#{service.name.split("::").last}' for '#{bundle_id}'")
         
     | 
| 
      
 46 
     | 
    
         
            +
                  #     app.update_service(Spaceship::Portal::AppService.new(service.identifier, value))
         
     | 
| 
      
 47 
     | 
    
         
            +
                  #   end
         
     | 
| 
      
 48 
     | 
    
         
            +
                  # end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                  app
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
              end
         
     | 
| 
      
 53 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,278 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Spaceship
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Portal
         
     | 
| 
      
 3 
     | 
    
         
            +
                class Certificate
         
     | 
| 
      
 4 
     | 
    
         
            +
                  # The PLIST request for the free certificate returns the certificate content
         
     | 
| 
      
 5 
     | 
    
         
            +
                  # in the certContent variable. It's stored in this attribute for later use.
         
     | 
| 
      
 6 
     | 
    
         
            +
                  attr_accessor :motionprovisioning_certContent,
         
     | 
| 
      
 7 
     | 
    
         
            +
                                :motionprovisioning_serialNumber
         
     | 
| 
      
 8 
     | 
    
         
            +
                end
         
     | 
| 
      
 9 
     | 
    
         
            +
              end
         
     | 
| 
      
 10 
     | 
    
         
            +
            end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            module MotionProvisioning
         
     | 
| 
      
 13 
     | 
    
         
            +
              class Certificate
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                attr_accessor :type, :output_path, :platform
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def client
         
     | 
| 
      
 18 
     | 
    
         
            +
                  MotionProvisioning.client
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                def mac?
         
     | 
| 
      
 22 
     | 
    
         
            +
                  self.platform == :mac
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                def certificate_name(type, platform)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  self.type = type
         
     | 
| 
      
 27 
     | 
    
         
            +
                  self.platform = platform
         
     | 
| 
      
 28 
     | 
    
         
            +
                  self.output_path = File.expand_path('./provisioning')
         
     | 
| 
      
 29 
     | 
    
         
            +
                  certificate_path = File.expand_path("./provisioning/#{platform}_#{type}_certificate.cer")
         
     | 
| 
      
 30 
     | 
    
         
            +
                  private_key_path = File.expand_path("./provisioning/#{platform}_#{type}_private_key.p12")
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                  # First check if there is a certificate and key file, and if it is installed
         
     | 
| 
      
 33 
     | 
    
         
            +
                  identities = available_identities
         
     | 
| 
      
 34 
     | 
    
         
            +
                  if File.exist?(certificate_path) && File.exist?(private_key_path) && ENV['recreate_certificate'].nil?
         
     | 
| 
      
 35 
     | 
    
         
            +
                    fingerprint = sha1_fingerprint(certificate_path)
         
     | 
| 
      
 36 
     | 
    
         
            +
                    installed_cert = identities.detect { |e| e[:fingerprint] == fingerprint }
         
     | 
| 
      
 37 
     | 
    
         
            +
                    if installed_cert
         
     | 
| 
      
 38 
     | 
    
         
            +
                      Utils.log("Info", "Using certificate '#{installed_cert[:name]}'.")
         
     | 
| 
      
 39 
     | 
    
         
            +
                      return installed_cert[:name]
         
     | 
| 
      
 40 
     | 
    
         
            +
                    else
         
     | 
| 
      
 41 
     | 
    
         
            +
                      # The certificate is not installed, so we install the cert and the key
         
     | 
| 
      
 42 
     | 
    
         
            +
                      import_file(private_key_path)
         
     | 
| 
      
 43 
     | 
    
         
            +
                      import_file(certificate_path)
         
     | 
| 
      
 44 
     | 
    
         
            +
                      name =  common_name(certificate_path)
         
     | 
| 
      
 45 
     | 
    
         
            +
                      Utils.log("Info", "Using certificate '#{name}'.")
         
     | 
| 
      
 46 
     | 
    
         
            +
                      return name
         
     | 
| 
      
 47 
     | 
    
         
            +
                    end
         
     | 
| 
      
 48 
     | 
    
         
            +
                  end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                  # Create the folder to store the certs
         
     | 
| 
      
 51 
     | 
    
         
            +
                  FileUtils.mkdir_p(File.expand_path('./provisioning'))
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                  # Make sure a client is created and logged in
         
     | 
| 
      
 54 
     | 
    
         
            +
                  client
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                  # All the certificates for the specified type
         
     | 
| 
      
 57 
     | 
    
         
            +
                  user_certificates = certificates
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                  # Lets see if any of the user certificates is in the keychain
         
     | 
| 
      
 60 
     | 
    
         
            +
                  installed_certificate = nil
         
     | 
| 
      
 61 
     | 
    
         
            +
                  if !certificates.empty?
         
     | 
| 
      
 62 
     | 
    
         
            +
                    installed_certs_sha1 = identities.map { |e| e[:fingerprint] }
         
     | 
| 
      
 63 
     | 
    
         
            +
                    installed_certificate = certificates.detect do |certificate|
         
     | 
| 
      
 64 
     | 
    
         
            +
                      sha1 = OpenSSL::Digest::SHA1.new(certificate.motionprovisioning_certContent || certificate.download_raw)
         
     | 
| 
      
 65 
     | 
    
         
            +
                      installed_certs_sha1.include?(sha1.to_s.upcase)
         
     | 
| 
      
 66 
     | 
    
         
            +
                    end
         
     | 
| 
      
 67 
     | 
    
         
            +
                  end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                  # There are no certificates in the server so we create a new one
         
     | 
| 
      
 70 
     | 
    
         
            +
                  if user_certificates.empty?
         
     | 
| 
      
 71 
     | 
    
         
            +
                    Utils.log("Warning", "Couldn't find any existing certificates... creating a new one.")
         
     | 
| 
      
 72 
     | 
    
         
            +
                    if certificate = create_certificate
         
     | 
| 
      
 73 
     | 
    
         
            +
                      return common_name(certificate)
         
     | 
| 
      
 74 
     | 
    
         
            +
                    else
         
     | 
| 
      
 75 
     | 
    
         
            +
                      Utils.log("Error", "Something went wrong when trying to create a new certificate.")
         
     | 
| 
      
 76 
     | 
    
         
            +
                      abort
         
     | 
| 
      
 77 
     | 
    
         
            +
                    end
         
     | 
| 
      
 78 
     | 
    
         
            +
                  # There are certificates in the server, but none is installed locally. Revoke all and create a new one.
         
     | 
| 
      
 79 
     | 
    
         
            +
                  elsif installed_certificate.nil?
         
     | 
| 
      
 80 
     | 
    
         
            +
                    Utils.log("Error", "None of the available certificates (#{user_certificates.count}) is installed on the local machine. Revoking...")
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                    # For distribution, ask before revoking
         
     | 
| 
      
 83 
     | 
    
         
            +
                    if self.type == :distribution && !$test_mode
         
     | 
| 
      
 84 
     | 
    
         
            +
                      answer = Utils.ask("Info", "There are #{user_certificates.count} distribution certificates in your account, but none installed locally.\n" \
         
     | 
| 
      
 85 
     | 
    
         
            +
                                "Before revoking and creating a new one, ask other team members who might have them installed to share them with you.\n" \
         
     | 
| 
      
 86 
     | 
    
         
            +
                                "Do you want to continue revoking the certificates? (Y/n):")
         
     | 
| 
      
 87 
     | 
    
         
            +
                      abort if answer.downcase != "y"
         
     | 
| 
      
 88 
     | 
    
         
            +
                    end
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                    # Revoke all and create new one
         
     | 
| 
      
 91 
     | 
    
         
            +
                    if MotionProvisioning.free
         
     | 
| 
      
 92 
     | 
    
         
            +
                      user_certificates.each do |certificate|
         
     | 
| 
      
 93 
     | 
    
         
            +
                        client.revoke_development_certificate(certificate.motionprovisioning_serialNumber)
         
     | 
| 
      
 94 
     | 
    
         
            +
                      end
         
     | 
| 
      
 95 
     | 
    
         
            +
                    else
         
     | 
| 
      
 96 
     | 
    
         
            +
                      user_certificates.each(&:revoke!)
         
     | 
| 
      
 97 
     | 
    
         
            +
                    end
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                    if certificate = create_certificate
         
     | 
| 
      
 100 
     | 
    
         
            +
                      return common_name(certificate)
         
     | 
| 
      
 101 
     | 
    
         
            +
                    else
         
     | 
| 
      
 102 
     | 
    
         
            +
                      Utils.log("Error", "Something went wrong when trying to create a new certificate...")
         
     | 
| 
      
 103 
     | 
    
         
            +
                      abort
         
     | 
| 
      
 104 
     | 
    
         
            +
                    end
         
     | 
| 
      
 105 
     | 
    
         
            +
                  # There are certificates on the server, and one of them is installed locally.
         
     | 
| 
      
 106 
     | 
    
         
            +
                  else
         
     | 
| 
      
 107 
     | 
    
         
            +
                    path = store_certificate_raw(installed_certificate.motionprovisioning_certContent ||installed_certificate.download_raw)
         
     | 
| 
      
 108 
     | 
    
         
            +
                    private_key_path = File.expand_path(File.join(output_path, "#{installed_certificate.id}.p12"))
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                    # This certificate is installed on the local machine
         
     | 
| 
      
 111 
     | 
    
         
            +
                    Utils.log("Info", "Using certificate '#{installed_certificate.name}'.")
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                    return common_name(path)
         
     | 
| 
      
 114 
     | 
    
         
            +
                  end
         
     | 
| 
      
 115 
     | 
    
         
            +
                end
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
      
 117 
     | 
    
         
            +
                # All certificates of this type
         
     | 
| 
      
 118 
     | 
    
         
            +
                def certificates
         
     | 
| 
      
 119 
     | 
    
         
            +
                  @certificates ||= begin
         
     | 
| 
      
 120 
     | 
    
         
            +
                    if MotionProvisioning.free
         
     | 
| 
      
 121 
     | 
    
         
            +
                      client.development_certificates(mac: mac?).map do |cert|
         
     | 
| 
      
 122 
     | 
    
         
            +
                        certificate = Spaceship::Portal::Certificate.factory(cert)
         
     | 
| 
      
 123 
     | 
    
         
            +
                        certificate.motionprovisioning_certContent = cert['certContent'].read
         
     | 
| 
      
 124 
     | 
    
         
            +
                        certificate.motionprovisioning_serialNumber = cert['serialNumber']
         
     | 
| 
      
 125 
     | 
    
         
            +
                        certificate
         
     | 
| 
      
 126 
     | 
    
         
            +
                      end
         
     | 
| 
      
 127 
     | 
    
         
            +
                    else
         
     | 
| 
      
 128 
     | 
    
         
            +
                      certificates = certificate_type.all
         
     | 
| 
      
 129 
     | 
    
         
            +
                      # Filter out development certificates belonging to other team members
         
     | 
| 
      
 130 
     | 
    
         
            +
                      if self.type == :development
         
     | 
| 
      
 131 
     | 
    
         
            +
                        user_id = MotionProvisioning.team['currentTeamMember']['teamMemberId']
         
     | 
| 
      
 132 
     | 
    
         
            +
                        certificates.select! { |c| c.owner_id == user_id }
         
     | 
| 
      
 133 
     | 
    
         
            +
                      end
         
     | 
| 
      
 134 
     | 
    
         
            +
                      certificates
         
     | 
| 
      
 135 
     | 
    
         
            +
                    end
         
     | 
| 
      
 136 
     | 
    
         
            +
                  end
         
     | 
| 
      
 137 
     | 
    
         
            +
                end
         
     | 
| 
      
 138 
     | 
    
         
            +
             
     | 
| 
      
 139 
     | 
    
         
            +
                # The kind of certificate we're interested in
         
     | 
| 
      
 140 
     | 
    
         
            +
                def certificate_type
         
     | 
| 
      
 141 
     | 
    
         
            +
                  cert_type = nil
         
     | 
| 
      
 142 
     | 
    
         
            +
                  case platform
         
     | 
| 
      
 143 
     | 
    
         
            +
                  when :ios, :tvos
         
     | 
| 
      
 144 
     | 
    
         
            +
                    cert_type = Spaceship.certificate.production
         
     | 
| 
      
 145 
     | 
    
         
            +
                    cert_type = Spaceship.certificate.in_house if Spaceship.client.in_house?
         
     | 
| 
      
 146 
     | 
    
         
            +
                    cert_type = Spaceship.certificate.development if self.type == :development
         
     | 
| 
      
 147 
     | 
    
         
            +
                  when :mac
         
     | 
| 
      
 148 
     | 
    
         
            +
                    cert_type = Spaceship.certificate.mac_development
         
     | 
| 
      
 149 
     | 
    
         
            +
                    cert_type = Spaceship.certificate.mac_app_distribution if self.type == :distribution
         
     | 
| 
      
 150 
     | 
    
         
            +
                    cert_type = Spaceship.certificate.developer_i_d_application if self.type == :developer_id
         
     | 
| 
      
 151 
     | 
    
         
            +
                  end
         
     | 
| 
      
 152 
     | 
    
         
            +
                  cert_type
         
     | 
| 
      
 153 
     | 
    
         
            +
                end
         
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
      
 155 
     | 
    
         
            +
                def create_certificate_signing_request
         
     | 
| 
      
 156 
     | 
    
         
            +
                  $motion_provisioninig_csr || Spaceship.certificate.create_certificate_signing_request
         
     | 
| 
      
 157 
     | 
    
         
            +
                end
         
     | 
| 
      
 158 
     | 
    
         
            +
             
     | 
| 
      
 159 
     | 
    
         
            +
                def create_certificate
         
     | 
| 
      
 160 
     | 
    
         
            +
                  # Create a new certificate signing request
         
     | 
| 
      
 161 
     | 
    
         
            +
                  csr, pkey = create_certificate_signing_request
         
     | 
| 
      
 162 
     | 
    
         
            +
             
     | 
| 
      
 163 
     | 
    
         
            +
                  # Store all that onto the filesystem
         
     | 
| 
      
 164 
     | 
    
         
            +
                  request_path = File.expand_path(File.join(self.output_path, "#{platform}_#{type}.certSigningRequest"))
         
     | 
| 
      
 165 
     | 
    
         
            +
                  File.write(request_path, csr.to_pem)
         
     | 
| 
      
 166 
     | 
    
         
            +
             
     | 
| 
      
 167 
     | 
    
         
            +
                  private_key_path = File.expand_path(File.join(self.output_path, "#{platform}_#{type}_private_key.p12"))
         
     | 
| 
      
 168 
     | 
    
         
            +
                  File.write(private_key_path, pkey.export)
         
     | 
| 
      
 169 
     | 
    
         
            +
             
     | 
| 
      
 170 
     | 
    
         
            +
                  # Use the signing request to create a new distribution certificate
         
     | 
| 
      
 171 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 172 
     | 
    
         
            +
                    certificate = nil
         
     | 
| 
      
 173 
     | 
    
         
            +
                    certificate_attrs = nil
         
     | 
| 
      
 174 
     | 
    
         
            +
                    if MotionProvisioning.free
         
     | 
| 
      
 175 
     | 
    
         
            +
                      certificate_attrs = client.create_development_certificate(csr.to_pem)
         
     | 
| 
      
 176 
     | 
    
         
            +
                      # Fetch the certificate again because the response does not contain
         
     | 
| 
      
 177 
     | 
    
         
            +
                      # the certContent key
         
     | 
| 
      
 178 
     | 
    
         
            +
                      certificate_attrs = client.development_certificates(mac: mac?).detect do |cert|
         
     | 
| 
      
 179 
     | 
    
         
            +
                        cert['certificateId'] == certificate_attrs['certificateId']
         
     | 
| 
      
 180 
     | 
    
         
            +
                      end
         
     | 
| 
      
 181 
     | 
    
         
            +
                      certificate_attrs['certificateTypeDisplayId'] = certificate_attrs['certificateType']['certificateTypeDisplayId']
         
     | 
| 
      
 182 
     | 
    
         
            +
                      certificate = Spaceship::Portal::Certificate.factory(certificate_attrs)
         
     | 
| 
      
 183 
     | 
    
         
            +
                      certificate.motionprovisioning_certContent = certificate_attrs['certContent'].read
         
     | 
| 
      
 184 
     | 
    
         
            +
                      certificate
         
     | 
| 
      
 185 
     | 
    
         
            +
                    else
         
     | 
| 
      
 186 
     | 
    
         
            +
                      certificate = certificate_type.create!(csr: csr)
         
     | 
| 
      
 187 
     | 
    
         
            +
                    end
         
     | 
| 
      
 188 
     | 
    
         
            +
                  rescue => ex
         
     | 
| 
      
 189 
     | 
    
         
            +
                    if ex.to_s.include?("You already have a current")
         
     | 
| 
      
 190 
     | 
    
         
            +
                      FileUtils.rm(private_key_path)
         
     | 
| 
      
 191 
     | 
    
         
            +
                      FileUtils.rm(request_path)
         
     | 
| 
      
 192 
     | 
    
         
            +
                      Utils.log("Error", "Could not create another certificate, reached the maximum number of available certificates. Manually revoke certificates in the Developer Portal.")
         
     | 
| 
      
 193 
     | 
    
         
            +
                      abort
         
     | 
| 
      
 194 
     | 
    
         
            +
                    end
         
     | 
| 
      
 195 
     | 
    
         
            +
                    raise ex
         
     | 
| 
      
 196 
     | 
    
         
            +
                  end
         
     | 
| 
      
 197 
     | 
    
         
            +
                  Utils.log("Info", "Successfully created certificate.")
         
     | 
| 
      
 198 
     | 
    
         
            +
             
     | 
| 
      
 199 
     | 
    
         
            +
                  cert_path = store_certificate_raw(certificate.motionprovisioning_certContent || certificate.download_raw)
         
     | 
| 
      
 200 
     | 
    
         
            +
             
     | 
| 
      
 201 
     | 
    
         
            +
                  # Import all the things into the Keychain
         
     | 
| 
      
 202 
     | 
    
         
            +
                  import_file(private_key_path)
         
     | 
| 
      
 203 
     | 
    
         
            +
                  import_file(cert_path)
         
     | 
| 
      
 204 
     | 
    
         
            +
                  Utils.log("Info", "Successfully installed certificate.")
         
     | 
| 
      
 205 
     | 
    
         
            +
             
     | 
| 
      
 206 
     | 
    
         
            +
                  if self.type == :distribution
         
     | 
| 
      
 207 
     | 
    
         
            +
                    Utils.log("Warning", "You have just created a distribution certificate. These certificates must be shared with other team members by sending them the private key (.p12) and certificate (.cer) files in your /provisioning folder and install them in the keychain.")
         
     | 
| 
      
 208 
     | 
    
         
            +
                  else
         
     | 
| 
      
 209 
     | 
    
         
            +
                    Utils.log("Warning", "You have just created a development certificate. If you want to use this certificate on another machine, transfer the private key (.p12) and certificate (.cer) files in your /provisioning folder and install them in the keychain.")
         
     | 
| 
      
 210 
     | 
    
         
            +
                  end
         
     | 
| 
      
 211 
     | 
    
         
            +
                  Utils.ask("Info", "Press any key to continue...") if !$test_mode
         
     | 
| 
      
 212 
     | 
    
         
            +
             
     | 
| 
      
 213 
     | 
    
         
            +
                  return cert_path
         
     | 
| 
      
 214 
     | 
    
         
            +
                end
         
     | 
| 
      
 215 
     | 
    
         
            +
             
     | 
| 
      
 216 
     | 
    
         
            +
                def store_certificate_raw(raw_data)
         
     | 
| 
      
 217 
     | 
    
         
            +
                  path = File.expand_path(File.join(self.output_path, "#{platform}_#{type}_certificate.cer"))
         
     | 
| 
      
 218 
     | 
    
         
            +
                  File.write(path, raw_data)
         
     | 
| 
      
 219 
     | 
    
         
            +
                  path
         
     | 
| 
      
 220 
     | 
    
         
            +
                end
         
     | 
| 
      
 221 
     | 
    
         
            +
             
     | 
| 
      
 222 
     | 
    
         
            +
                def available_identities
         
     | 
| 
      
 223 
     | 
    
         
            +
                  ids = []
         
     | 
| 
      
 224 
     | 
    
         
            +
                  keychain = $keychain || "#{Dir.home}/Library/Keychains/login.keychain"
         
     | 
| 
      
 225 
     | 
    
         
            +
                  available = `security find-identity -v -p codesigning #{keychain}`
         
     | 
| 
      
 226 
     | 
    
         
            +
                  available.split("\n").each do |current|
         
     | 
| 
      
 227 
     | 
    
         
            +
                    next if current.include? "REVOKED"
         
     | 
| 
      
 228 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 229 
     | 
    
         
            +
                      id = current.match(/.*\) (.*) \"(.*)\"/)
         
     | 
| 
      
 230 
     | 
    
         
            +
                      ids << {
         
     | 
| 
      
 231 
     | 
    
         
            +
                        fingerprint: id[1],
         
     | 
| 
      
 232 
     | 
    
         
            +
                        name: id[2]
         
     | 
| 
      
 233 
     | 
    
         
            +
                      }
         
     | 
| 
      
 234 
     | 
    
         
            +
                    rescue
         
     | 
| 
      
 235 
     | 
    
         
            +
                      # the last line does not match
         
     | 
| 
      
 236 
     | 
    
         
            +
                    end
         
     | 
| 
      
 237 
     | 
    
         
            +
                  end
         
     | 
| 
      
 238 
     | 
    
         
            +
                  ids
         
     | 
| 
      
 239 
     | 
    
         
            +
                end
         
     | 
| 
      
 240 
     | 
    
         
            +
             
     | 
| 
      
 241 
     | 
    
         
            +
                def sha1_fingerprint(path)
         
     | 
| 
      
 242 
     | 
    
         
            +
                  result = `openssl x509 -in "#{path}" -inform der -noout -sha1 -fingerprint`
         
     | 
| 
      
 243 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 244 
     | 
    
         
            +
                    result = result.match(/SHA1 Fingerprint=(.*)/)[1]
         
     | 
| 
      
 245 
     | 
    
         
            +
                    result.delete!(':')
         
     | 
| 
      
 246 
     | 
    
         
            +
                    return result
         
     | 
| 
      
 247 
     | 
    
         
            +
                  rescue
         
     | 
| 
      
 248 
     | 
    
         
            +
                    puts result
         
     | 
| 
      
 249 
     | 
    
         
            +
                    Utils.log("Error", "Error parsing certificate '#{path}'")
         
     | 
| 
      
 250 
     | 
    
         
            +
                  end
         
     | 
| 
      
 251 
     | 
    
         
            +
                end
         
     | 
| 
      
 252 
     | 
    
         
            +
             
     | 
| 
      
 253 
     | 
    
         
            +
                def common_name(path)
         
     | 
| 
      
 254 
     | 
    
         
            +
                  result = `openssl x509 -in "#{path}" -inform der -noout -sha1 -subject`
         
     | 
| 
      
 255 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 256 
     | 
    
         
            +
                    return result.match(/\/CN=(.*?)\//)[1]
         
     | 
| 
      
 257 
     | 
    
         
            +
                  rescue
         
     | 
| 
      
 258 
     | 
    
         
            +
                    puts result
         
     | 
| 
      
 259 
     | 
    
         
            +
                    Utils.log("Error", "Error parsing certificate '#{path}'")
         
     | 
| 
      
 260 
     | 
    
         
            +
                  end
         
     | 
| 
      
 261 
     | 
    
         
            +
                end
         
     | 
| 
      
 262 
     | 
    
         
            +
             
     | 
| 
      
 263 
     | 
    
         
            +
                def import_file(path)
         
     | 
| 
      
 264 
     | 
    
         
            +
                  unless File.exist?(path)
         
     | 
| 
      
 265 
     | 
    
         
            +
                    Utils.log("Error", "Could not find file '#{path}'")
         
     | 
| 
      
 266 
     | 
    
         
            +
                    abort
         
     | 
| 
      
 267 
     | 
    
         
            +
                  end
         
     | 
| 
      
 268 
     | 
    
         
            +
             
     | 
| 
      
 269 
     | 
    
         
            +
                  keychain = $keychain || "#{Dir.home}/Library/Keychains/login.keychain"
         
     | 
| 
      
 270 
     | 
    
         
            +
             
     | 
| 
      
 271 
     | 
    
         
            +
                  command = "security import #{path.shellescape} -k '#{keychain}' -P ''"
         
     | 
| 
      
 272 
     | 
    
         
            +
                  command << " -T /usr/bin/codesign"
         
     | 
| 
      
 273 
     | 
    
         
            +
                  command << " -T /usr/bin/security"
         
     | 
| 
      
 274 
     | 
    
         
            +
             
     | 
| 
      
 275 
     | 
    
         
            +
                  `#{command} 2>&1`
         
     | 
| 
      
 276 
     | 
    
         
            +
                end
         
     | 
| 
      
 277 
     | 
    
         
            +
              end
         
     | 
| 
      
 278 
     | 
    
         
            +
            end
         
     |