class FruityBuilder::IOS::Signing

Namespace for all methods encapsulating idevice calls

Public Class Methods

enable_get_tasks(app_path) click to toggle source
# File lib/fruity_builder/signing.rb, line 106
def self.enable_get_tasks(app_path)
  entitlements = get_entitlements(app_path, raw: true)

  return entitlements if entitlements.scan(/<key>get-task-allow<\/key>\\n.*<true\/>/).count > 0

  entitlements.gsub('<false/>', '<true/>') if entitlements.scan(/<key>get-task-allow<\/key>\\n.*<false\/>/)
end
get_entitlements(app_path, raw = false) click to toggle source
# File lib/fruity_builder/signing.rb, line 89
def self.get_entitlements(app_path, raw = false)
  app_path = unpack_ipa(app_path) if is_ipa?(app_path)
  result = execute("codesign -d --entitlements - #{app_path}")

  if result.exit != 0
    raise SigningCommandError.new(result.stderr)
  end

  # Clean up the result as it occasionally contains invalid UTF-8 characters
  xml = result.stdout.to_s.encode('UTF-8', 'UTF-8', invalid: :replace)
  xml = xml[xml.index('<')..xml.length]

  return xml if raw
  entitlements = Plistutil.parse_xml(xml)
  return entitlements
end
get_signing_certs() click to toggle source
# File lib/fruity_builder/signing.rb, line 75
def self.get_signing_certs
  result = execute('security find-identity -p codesigning -v')

  raise SigningCommandError.new(result.stderr) if result.exit != 0

  certs = []
  result.stdout.split("\n").each do |line|
    if /\)\s*(\S*)\s*"(.*)"/.match(line)
      certs << { id: Regexp.last_match[1], name: Regexp.last_match[2] }
    end
  end
  certs
end
is_app_signed?(app_path) click to toggle source
# File lib/fruity_builder/signing.rb, line 28
def self.is_app_signed?(app_path)
  app_path = unpack_ipa(app_path) if is_ipa?(app_path)
  result = execute("codesign -d -vvvv '#{app_path}'")

  if result.exit != 0
    return false if /is not signed/.match(result.stderr)
    raise SigningCommandError.new(result.stderr)
  end

  true
end
is_ipa?(path) click to toggle source

Check to see if the path is an IPA

# File lib/fruity_builder/signing.rb, line 10
def self.is_ipa?(path)
  return true if (File.extname path).downcase == '.ipa'
  false
end
sign_app(options = {}) click to toggle source
# File lib/fruity_builder/signing.rb, line 40
def self.sign_app(options = {})
  cert          = options[:cert]
  entitlements  = options[:entitlements]
  app           = options[:app]
  original_app  = nil

  if is_ipa?(app)
    original_app = app
    app = unpack_ipa(app)
  end

  command = "codesign --force --sign '#{cert}'"
  # Check to see if the entitlements passed in is a file or the XML
  unless File.exists?(entitlements)
    file = Tempfile.new('entitlements')
    file.write(entitlements)
    file.close
    entitlements = file.path
    command = command + " --entitlements #{entitlements}"
  end

  result = execute(command + " '#{app}'")

  raise SigningCommandError.new(result.stderr) if result.exit != 0

  zip_app(app, original_app) if original_app
end
unpack_ipa(path) click to toggle source
# File lib/fruity_builder/signing.rb, line 15
def self.unpack_ipa(path)
  folder = File.dirname(path)
  target = (File.basename path, (File.extname path))

  # Check to see if the target has already been unzipped
  return Dir["#{folder}/#{target}/Payload/*.app"].first if File.exists? ("#{folder}/#{target}/Payload/")

  result = execute("unzip '#{path}' -d '#{folder}/#{target}'")
  raise SigningCommandError.new(result.stderr) if result.exit != 0

  Dir["#{folder}/#{target}/Payload/*.app"].first
end
zip_app(payload_path, ipa_path) click to toggle source
# File lib/fruity_builder/signing.rb, line 68
def self.zip_app(payload_path, ipa_path)
  result = execute("cd #{File.dirname(payload_path)}; zip -r #{ipa_path} ../Payload ")
  raise SigningCommandError.new(result.stderr) if result.exit != 0

  true
end