class Escalator::Resign

Attributes

assets_path[RW]
bundle_version[RW]
cerIdentifier[RW]
command[RW]
iconInfo_path[RW]
keyInfo_path[RW]
tmpdir[RW]

Public Class Methods

run(command) click to toggle source
# File lib/escalator/resign.rb, line 7
def run command
  @command = command
  prepareContext
  applyCertificates
  command.ipaPaths.map { |path|
    resignIPA handleIPA path
  }
end

Private Class Methods

applyCertificates() click to toggle source
# File lib/escalator/resign.rb, line 58
def applyCertificates
  Throw.note "begin apply certificates ..."
  cmd = "fastlane cert create --api_key_path #{keyInfo_path}"
  cmd += " --generate_apple_certs true -p #{command.user_password} -o #{tmpdir}"
  system cmd
  if !$?.success?
    Throw.error "Apply .cer file faild!"
  end
  cer = File.read Dir.glob("#{tmpdir}/*.cer").first
  @cerIdentifier = OpenSSL::Digest.hexdigest("SHA1", cer).upcase
  cmd = "fastlane sigh renew --api_key_path #{keyInfo_path} -a #{command.bundle_identifier}"
  cmd += " -q embedded.mobileprovision -o #{tmpdir}"
  system cmd
  if !$?.success?
    Throw.error "Apply embedded.mobileprovision file faild!"
  end
end
handleAssets(appPath, assetsPath) click to toggle source
# File lib/escalator/resign.rb, line 149
def handleAssets appPath, assetsPath
  Throw.note "begin handle assets ..."
  cmd = "actool --compile #{assets_path} --minimum-deployment-target 11.0 --platform iphoneos"
  cmd += " --app-icon AppIcon --output-partial-info-plist #{iconInfo_path}"
  cmd += " --compress-pngs #{command.iconAssets_path}"
  Dir.glob("#{assetsPath}/*.xcassets") { |file|
    cmd += " #{file}"
    icon_path = "#{file}/AppIcon.appiconset"
    if File.exist? icon_path
      FileUtils.rm_rf icon_path
    end
  }
  system cmd
  if !$?.success?
    Throw.error "Generate new Assets.car faild!"
  end
  FileUtils.mv Dir["#{assets_path}/*"], appPath, force: true
end
handleBundle(appPath, newBundleName, newExecutableName) click to toggle source
# File lib/escalator/resign.rb, line 223
def handleBundle appPath, newBundleName, newExecutableName
  Throw.note "begin handle bundle ..."
  plistPath = appPath + "/Info.plist"
  plist = CFPropertyList::List.new :file => plistPath
  data = CFPropertyList.native_types plist.value
  if !newExecutableName.empty?
    executableName = data["CFBundleExecutable"]
    FileUtils.mv "#{appPath}/#{executableName}", "#{appPath}/#{newExecutableName}", force: true
    data["CFBundleExecutable"] = newExecutableName
  end
  if !newBundleName.empty?
    data["CFBundleName"] = newBundleName
  end
  plist.value = CFPropertyList::guess data
  plist.save plistPath
  if !newBundleName.empty?
    newAppPath = File.dirname(appPath) + "/#{newBundleName}.app"
    FileUtils.mv appPath, newAppPath, force: true
  end
end
handleIPA(ipaPath) click to toggle source
# File lib/escalator/resign.rb, line 76
def handleIPA ipaPath
  basePath = File.dirname(ipaPath)
  coverInfoPath = basePath + "/CoverInfo.plist"
  coverData = Hash.new
  coverData["CFBundleVersion"] = ""
  coverData["CFBundleName"] = ""
  coverData["CFBundleExecutable"] = ""
  coverData["CFBundleDisplayName"] = ""
  if File.exist? coverInfoPath
    coverPlist = CFPropertyList::List.new :file => coverInfoPath
    CFPropertyList.native_types(coverPlist.value).each { |key, value|
      coverData[key] = value
    }
  end
  bundleVersion = coverData["CFBundleVersion"]
  if bundleVersion.empty?
    bundleVersion = bundle_version.to_s
    @bundle_version += 1
  end
  displayName = coverData["CFBundleDisplayName"]
  if displayName.empty?
    displayName = command.display_name
  end
  bundleName = coverData["CFBundleName"]
  if bundleName.empty?
    bundleName = command.bundle_name
  end
  executableName = coverData["CFBundleExecutable"]
  if executableName.empty?
    executableName = command.executable_name
  end
  if (command.iconAssets_path.empty? &&
      bundleName.empty? &&
      executableName.empty?)
    return OpenStruct.new(:path => ipaPath, :displayName => displayName)
  end
  Throw.note "begin handle #{File.basename ipaPath} ..."
  content_path = tmpdir + "/content"
  FileUtils.mkdir_p content_path
  system "unzip -o -q #{ipaPath} -d #{content_path}"
  if !$?.success?
    Throw.error "Unzip ipa faild!"
  end
  appPath = Dir.glob("#{content_path}/**/*.app").first
  if !command.iconAssets_path.empty?
    handleAssets appPath, basePath
  end
  pluginsProvisions = []
  pluginsPath = "#{appPath}/PlugIns"
  if File.exist? pluginsPath
    pluginsProvisions = handlePlugins pluginsPath
  end
  if !(bundleName.empty? && executableName.empty?)
    handleBundle appPath, bundleName, executableName
  end
  FileUtils.rm_rf ipaPath
  newIpaPath = ipaPath
  if !bundleName.empty?
    newIpaPath = File.expand_path "../#{bundleName}.ipa", ipaPath
  end
  system "cd #{content_path} && zip -q -r -D #{newIpaPath} ./*"
  if !$?.success?
    Throw.error "Zip payload faild!"
  end
  FileUtils.rm_rf content_path
  OpenStruct.new(
    :path => newIpaPath,
    :bundleVersion => bundleVersion,
    :displayName => displayName,
    :pluginsProvisions => pluginsProvisions
  )
end
handlePlugins(path) click to toggle source
# File lib/escalator/resign.rb, line 168
def handlePlugins path
  appleIdField = command.apple_id.empty? ? "" : "-u #{command.apple_id}"
  parts = (command.bundle_identifier.split ".").sort_by { |p| p.length }
  groupName = parts.last
  groupIdentifier = "group.#{command.bundle_identifier}"
  system "fastlane produce group #{appleIdField} -g #{groupIdentifier} -n '#{groupName} App Group'"
  if !$?.success?
    Throw.error "Create #{groupName} app group faild!"
  end
  infos = Dir.glob("#{path}/*.appex/Info.plist").map { |infoPath|
    plist = CFPropertyList::List.new :file => infoPath
    CFPropertyList.native_types plist.value
  }
  enableNetworkExtension = false
  infos.each { |info|
    enableNetworkExtension |= info["NSExtension"]["NSExtensionPointIdentifier"].include? "com.apple.networkextension"
  }
  networkExtensionField = enableNetworkExtension ? "--network-extension" : ""
  system "fastlane produce enable_services #{appleIdField} --app-group #{networkExtensionField} -j ios -a #{command.bundle_identifier}"
  if !$?.success?
    Throw.error "#{command.bundle_identifier} enable services faild!"
  end
  system "fastlane produce associate_group #{appleIdField} #{groupIdentifier} -a #{command.bundle_identifier}"
  if !$?.success?
    Throw.error "#{command.bundle_identifier} associate group faild!"
  end
  system "fastlane sigh renew --api_key_path #{keyInfo_path} -f -a #{command.bundle_identifier} -q embedded.mobileprovision -o #{tmpdir}"
  if !$?.success?
    Throw.error "Apply embedded.mobileprovision file faild!"
  end
  infos.map { |info|
    originalIdentifier = info["CFBundleIdentifier"]
    name = originalIdentifier.split(".").last
    provisionName = "#{name}.mobileprovision"
    identifier = "#{command.bundle_identifier}.#{name}"
    system "fastlane produce create #{appleIdField} -i -j ios -a #{identifier} -q #{name}"
    if !$?.success?
      Throw.error "Create #{identifier} faild!"
    end
    system "fastlane produce enable_services #{appleIdField} --app-group #{networkExtensionField} -j ios -a #{identifier}"
    if !$?.success?
      Throw.error "#{identifier} enable services faild!"
    end
    system "fastlane produce associate_group #{appleIdField} #{groupIdentifier} -a #{identifier}"
    if !$?.success?
      Throw.error "#{identifier} associate group faild!"
    end
    system "fastlane sigh renew --api_key_path #{keyInfo_path} -a #{identifier} -q #{provisionName} -o #{tmpdir}"
    if !$?.success?
      Throw.error "Apply #{provisionName} file faild!"
    end
    "#{originalIdentifier}=#{tmpdir}/#{provisionName}"
  }
end
prepareContext() click to toggle source
# File lib/escalator/resign.rb, line 32
def prepareContext
  Throw.note "Prepare resign context ..."
  @tmpdir = ENV["TMPDIR"] + "escalator/resign"
  if File.exist? tmpdir
    FileUtils.rm_rf Dir["#{tmpdir}/*"]
  else
    FileUtils.mkdir_p tmpdir
  end
  @keyInfo_path = tmpdir + "/keyInfo.json"
  keyInfo = Hash.new
  keyInfo[:in_house] = false
  keyInfo[:key_id] = command.key_id
  keyInfo[:issuer_id] = command.issuer_id
  keyInfo[:key_filepath] = command.keyfile_path
  keyInfo[:key] = File.read command.keyfile_path
  file = File.new keyInfo_path, "w"
  file.write JSON.generate keyInfo
  file.close
  @bundle_version = command.bundle_version.to_i
  @assets_path = tmpdir + "/assets"
  FileUtils.mkdir_p assets_path
  info_path = File.expand_path "../../../resources/IconInfo.plist", __FILE__
  FileUtils.cp info_path, tmpdir
  @iconInfo_path = tmpdir + "/IconInfo.plist"
end
resignIPA(ipaInfo) click to toggle source
# File lib/escalator/resign.rb, line 244
def resignIPA ipaInfo
  path = File.dirname ipaInfo.path
  ipaName = File.basename ipaInfo.path
  Throw.note "begin resign #{ipaName} ..."
  cmd = "fastlane sigh resign --short_version #{command.short_version}"
  cmd += " --bundle_version #{ipaInfo.bundleVersion}"
  if !ipaInfo.displayName.empty?
     cmd += " -d #{ipaInfo.displayName}"
  end
  cmd += " --use_app_entitlements -p #{tmpdir}/embedded.mobileprovision"
  ipaInfo.pluginsProvisions.each { |provision|
    cmd += " -p #{provision}"
  }
  cmd += " -i #{cerIdentifier} #{ipaInfo.path}"
  system cmd
  if !$?.success?
    Throw.error "Resign ipa faild!"
  end
  newPath = path + "_build_#{ipaInfo.bundleVersion}"
  FileUtils.mv path, newPath, force: true
  "#{newPath}/#{ipaName}"
end