class DeployGate::Xcode::Export

Constants

AD_HOC
ENTERPRISE
PROFILE_EXTNAME
SUPPORT_EXPORT_METHOD

Public Class Methods

adhoc?(profile_path) click to toggle source

@param [String] profile_path @return [Boolean]

# File lib/deploygate/xcode/export.rb, line 176
def adhoc?(profile_path)
  profile = profile_to_plist(profile_path)
  !profile['Entitlements']['get-task-allow'] && profile['ProvisionsAllDevices'].nil?
end
check_local_certificates() click to toggle source
# File lib/deploygate/xcode/export.rb, line 248
def check_local_certificates
  if installed_distribution_certificate_ids.count == 0
    # not local install certificate
    puts HighLine.color(I18n.t('xcode.export.check_local_certificates.not_local_install_certificate.error_message'), HighLine::RED)
    puts ''
    puts I18n.t('xcode.export.check_local_certificates.not_local_install_certificate.note')
    puts ''
    exit
  end

  iphone_conflicting_certificates = installed_distribution_conflicting_certificates_by('iPhone Distribution')
  apple_conflicting_certificates = installed_distribution_conflicting_certificates_by('Apple Distribution')
  if iphone_conflicting_certificates.count > 0 || apple_conflicting_certificates.count > 0
    puts HighLine.color(I18n.t('xcode.export.check_local_certificates.conflict_certificate.error_message'), HighLine::RED)
    puts ''
    puts I18n.t('xcode.export.check_local_certificates.conflict_certificate.note')
    iphone_conflicting_certificates.each do |certificate|
      puts certificate
    end
    apple_conflicting_certificates.each do |certificate|
      puts certificate
    end
    puts ""

    exit
  end
end
clean_provisioning_profiles(bundle_identifier, team) click to toggle source

@param [String] bundle_identifier @return [void]

# File lib/deploygate/xcode/export.rb, line 278
def clean_provisioning_profiles(bundle_identifier, team)
  puts I18n.t('xcode.export.clean_provisioning_profiles.start')
  puts ''

  profile_paths = []
  profile_paths = load_profile_paths
  profiles = profile_paths.map{|p| profile_to_plist(p)}

  profiles.each do |profile|
    entities = profile['Entitlements']
    unless entities['get-task-allow']
      team = entities['com.apple.developer.team-identifier']
      application_id = entities['application-identifier']
      if "#{team}.#{bundle_identifier}" == application_id &&
          DateTime.now < profile['ExpirationDate'] &&
          installed_certificate?(profile['Path'])

        profile_paths.push(profile['Path'])
      end
    end
  end

  most_new_profile_path = profile_paths.first
  profile_paths.each do |path|
    most_new_profile_path = path if File.ctime(path) > File.ctime(most_new_profile_path)
  end

  profile_paths.delete(most_new_profile_path)
  profile_paths.each do |path|
    next unless File.exist?(path)
    File.delete(path)
    puts I18n.t('xcode.export.clean_provisioning_profiles.delete', path: path)
  end

  puts ''
  puts I18n.t('xcode.export.clean_provisioning_profiles.finish')
end
codesigning_identity(profile_path) click to toggle source

@param [String] profile_path @return [String]

# File lib/deploygate/xcode/export.rb, line 145
def codesigning_identity(profile_path)
  profile = profile_to_plist(profile_path)
  identity = nil

  profile['DeveloperCertificates'].each do |cert|
    certificate_str = cert.read
    certificate =  OpenSSL::X509::Certificate.new certificate_str
    id = OpenSSL::Digest::SHA1.new(certificate.to_der).to_s.upcase!

    available = `security find-identity -v -p codesigning`
    available.split("\n").each do |current|
      next if current.include? "REVOKED"
      begin
        search = current.match(/.*\) (.*) \"(.*)\"/)
        identity = search[2] if id == search[1]
      rescue
      end
    end
  end

  identity
end
create_provisioning(identifier, uuid, team_id) click to toggle source
# File lib/deploygate/xcode/export.rb, line 205
def create_provisioning(identifier, uuid, team_id)
  member_center = Xcode::MemberCenter.new(team_id)
  app = MemberCenters::App.new(identifier, member_center)
  provisioning_prifile = MemberCenters::ProvisioningProfile.new(identifier, member_center)

  begin
    unless app.created?
      app.create!
      puts I18n.t('xcode.export.create_provisioning.created', identifier: identifier)
    end
  rescue => e
    puts HighLine.color(I18n.t('xcode.export.create_provisioning.error.failed_to_create.app_id'), HighLine::RED)
    raise e
  end

  begin
    provisioning_profiles = provisioning_prifile.create!(uuid)
  rescue => e
    puts HighLine.color(I18n.t('xcode.export.create_provisioning.error.failed_to_create.provisioning_profile'), HighLine::RED)
    raise e
  end

  select_profile(provisioning_profiles)
end
find_local_data(bundle_identifier, uuid = nil, provisioning_team = nil, specifier_name = nil) click to toggle source

@param [String] bundle_identifier @param [String] uuid @param [String] provisioning_team @return [LocalTeams]

# File lib/deploygate/xcode/export.rb, line 35
def find_local_data(bundle_identifier, uuid = nil, provisioning_team = nil, specifier_name = nil)
  local_teams = LocalTeams.new

  profile_paths = load_profile_paths
  profiles = profile_paths.map{|p| profile_to_plist(p)}
  profiles.reject! {|profile| profile['UUID'] != uuid} unless uuid.nil?
  profiles.reject! {|profile| profile['Name'] != specifier_name} unless specifier_name.nil?

  profiles.each do |profile|
    next if DateTime.now >= profile['ExpirationDate'] || !installed_certificate?(profile['Path'])

    entities = profile['Entitlements']
    unless entities['get-task-allow']
      team_id = entities['com.apple.developer.team-identifier']
      next if provisioning_team != nil && team_id != provisioning_team

      application_id = entities['application-identifier']
      application_id.slice!(/^#{team_id}\./)
      application_id = '.' + application_id if application_id == '*'
      if match = bundle_identifier.match(application_id)
        next if match[0] != bundle_identifier

        local_teams.add(team_id, profile['TeamName'], profile['Path'])
      end
    end
  end

  local_teams
end
inhouse?(profile_path) click to toggle source

@param [String] profile_path @return [Boolean]

# File lib/deploygate/xcode/export.rb, line 183
def inhouse?(profile_path)
  profile = profile_to_plist(profile_path)
  !profile['Entitlements']['get-task-allow'] && !profile['ProvisionsAllDevices'].nil?
end
installed_certificate?(profile_path) click to toggle source

@param [String] profile_path @return [Boolean]

# File lib/deploygate/xcode/export.rb, line 67
def installed_certificate?(profile_path)
  profile = profile_to_plist(profile_path)
  certs = profile['DeveloperCertificates'].map do |cert|
    certificate_str = cert.read
    certificate =  OpenSSL::X509::Certificate.new certificate_str
    id = OpenSSL::Digest::SHA1.new(certificate.to_der).to_s.upcase!
    installed_distribution_certificate_ids.include?(id)
  end
  certs.include?(true)
end
installed_certificates() click to toggle source

@return [Array]

# File lib/deploygate/xcode/export.rb, line 120
def installed_certificates
  available = `security find-identity -v -p codesigning`
  certificates = []
  available.split("\n").each do |current|
    next if current.include? "REVOKED"
    certificates << current
  end

  certificates
end
installed_distribution_certificate_ids() click to toggle source

@return [Array]

# File lib/deploygate/xcode/export.rb, line 79
def installed_distribution_certificate_ids
  certificates = installed_certificates()
  ids = []
  certificates.each do |current|
    next unless current.match(/iPhone Distribution:/) || current.match(/Apple Distribution:/)
    begin
      (ids << current.match(/.*\) (.*) \".*/)[1])
    rescue
      # the last line does not match
    end
  end

  ids
end
installed_distribution_conflicting_certificates_by(distribution_name) click to toggle source

@return [Array]

# File lib/deploygate/xcode/export.rb, line 95
def installed_distribution_conflicting_certificates_by(distribution_name)
  certificates = installed_certificates()
  names = []
  certificates.each do |current|
    begin
      names << current.match(/(#{distribution_name}:.*)/)[1]
    rescue
    end
  end

  conflicting_names = names.select{|e| names.index(e) != names.rindex(e)}.uniq
  conflicting_certificates = []
  certificates.each do |current|
    begin
      name = current.match(/(#{distribution_name}:.*)/)[1]
      next unless conflicting_names.include?(name)
      conflicting_certificates << current
    rescue
    end
  end

  conflicting_certificates
end
load_profile_paths() click to toggle source
# File lib/deploygate/xcode/export.rb, line 188
def load_profile_paths
  profiles_path = File.expand_path("~") + "/Library/MobileDevice/Provisioning Profiles/*.mobileprovision"
  Dir[profiles_path]
end
method(profile_path) click to toggle source

@param [String] profile_path @return [String]

# File lib/deploygate/xcode/export.rb, line 170
def method(profile_path)
  adhoc?(profile_path) ? AD_HOC : ENTERPRISE
end
profile_to_plist(profile_path) click to toggle source

@param [String] profile_path @return [Hash]

# File lib/deploygate/xcode/export.rb, line 195
def profile_to_plist(profile_path)
  File.open(profile_path) do |profile|
    asn1 = OpenSSL::ASN1.decode_all(profile.read).first
    plist_str = asn1.value[1].value[0].value[2].value[1].value[0].value
    plist = Plist.parse_xml plist_str.force_encoding('UTF-8')
    plist['Path'] = profile_path
    return plist
  end
end
provisioning_profile(bundle_identifier, uuid = nil, provisioning_team = nil, specifier_name = nil) click to toggle source

@param [String] bundle_identifier @param [String] uuid @param [String] provisioning_team @return [String]

# File lib/deploygate/xcode/export.rb, line 15
def provisioning_profile(bundle_identifier, uuid = nil, provisioning_team = nil, specifier_name = nil)
  local_teams = DeployGate::Xcode::Export.find_local_data(bundle_identifier, uuid, provisioning_team, specifier_name)

  case local_teams.teams_count
    when 0
      target_provisioning_profile = create_provisioning(bundle_identifier, uuid, provisioning_team)
    when 1
      target_provisioning_profile = select_profile(local_teams.first_team_profile_paths)
    else
      # when many teams
      target_provisioning_profile = select_teams(local_teams)
  end

  target_provisioning_profile
end
select_profile(profile_paths) click to toggle source

@param [Array] profile_paths @return [String]

# File lib/deploygate/xcode/export.rb, line 133
def select_profile(profile_paths)
  select = nil

  profile_paths.each do |path|
    select = path if adhoc?(path) && select.nil?
    select = path if inhouse?(path)
  end
  select
end
select_teams(local_teams) click to toggle source

@param [LocalTeams] local_teams @return [String]

# File lib/deploygate/xcode/export.rb, line 232
def select_teams(local_teams)
  result = nil
  cli = HighLine.new
  cli.choose do |menu|
    menu.prompt = I18n.t('xcode.export.select_teams.prompt')
    local_teams.teams.each do |team|
      menu.choice(I18n.t('xcode.export.select_teams.choice', team_name: team[:name], team_id: team[:id])) {
        profile_paths = local_teams.profile_paths(team[:id])
        result = DeployGate::Xcode::Export.select_profile(profile_paths)
      }
    end
  end

  result
end