module Fastlane::Helper::AndroidChannelsHelper

Constants

APKSIGNER_COMMAND_KEYS

Public Class Methods

apksigner_args(params) click to toggle source

解析并合并 apksigner 的参数, 额外的参数会覆盖之前设置过的值

# File lib/fastlane/plugin/android_channels/helper/android_channels_helper.rb, line 94
def self.apksigner_args(params)
  command = {}
  params.all_keys.each do |key|
    if APKSIGNER_COMMAND_KEYS.has_key?(key)
      if command_value = params[key]
        command_key = APKSIGNER_COMMAND_KEYS[key].to_s
        command_value = "pass:#{command_value}" if command_key.end_with?("pass")
        command[command_key] = command_value.shellescape
      end
    end
  end

  if extra_args = params[:apksigner_extra_args]
    extra_args.split(" ").each_slice(2) do |part|
      key = part[0].to_s.strip.shellescape
      if value = part[1]
        command[key.to_s] = value.strip.shellescape
      end
    end
  end

  command.flatten.join(" ")
end
apksigner_path(build_tools_path) click to toggle source
# File lib/fastlane/plugin/android_channels/helper/android_channels_helper.rb, line 198
def self.apksigner_path(build_tools_path)
  file = File.join(build_tools_path, "apksigner")

  UI.user_error!("Not found apksigner: #{params[:android_sdk_path]}") unless file && File.file?(file)
  file
end
build_tools_path(android_sdk_path, version = nil) click to toggle source
# File lib/fastlane/plugin/android_channels/helper/android_channels_helper.rb, line 186
def self.build_tools_path(android_sdk_path, version = nil)
  build_tools_path = File.join(android_sdk_path, "build-tools")
  unless version
    latest_path = Dir.glob("#{build_tools_path}/*").inject {|latest, current| File.basename(latest) > File.basename(current) ? latest : current }
    version = File.basename(latest_path)
  end
  path = File.join(build_tools_path, version)

  UI.user_error!("Not found build tools path: #{build_tools_path}") unless path && Dir.exist?(path)
  path
end
determine_apk_file!(params) click to toggle source
# File lib/fastlane/plugin/android_channels/helper/android_channels_helper.rb, line 118
def self.determine_apk_file!(params)
  apk_file = find_file(params[:apk_file])
  UI.user_error!("Not found apk file: #{params[:apk_file]}") unless apk_file
  apk_file
end
determine_apksigner!(params) click to toggle source
# File lib/fastlane/plugin/android_channels/helper/android_channels_helper.rb, line 154
def self.determine_apksigner!(params)
  android_sdk_path = params[:android_sdk_path]
  build_tools_version = params[:build_tools_version]
  UI.user_error!("Not found android SDK path: #{android_sdk_path}") unless android_sdk_path

  build_tools_path = build_tools_path(android_sdk_path, build_tools_version)
  apksigner_path(build_tools_path)
end
determine_channels!(params) click to toggle source
# File lib/fastlane/plugin/android_channels/helper/android_channels_helper.rb, line 124
def self.determine_channels!(params)
  channels = params[:channels]
  if (file = params[:channel_file]) && File.exist?(file)
    channels.concat(load_channel_file(file))
  end

  UI.user_error!("Empty channels") if channels.size.zero?
  channels.map{|n| n.strip}.uniq
end
determine_keystore!(params) click to toggle source
# File lib/fastlane/plugin/android_channels/helper/android_channels_helper.rb, line 134
def self.determine_keystore!(params)
  keystore = find_file(params[:keystore])
  UI.user_error!("Not found keystore file: #{params[:keystore]}") unless keystore
  keystore
end
determine_output_path!(params) click to toggle source
# File lib/fastlane/plugin/android_channels/helper/android_channels_helper.rb, line 140
def self.determine_output_path!(params)
  output_path = params[:output_path]
  if Dir.exist?(output_path)
    if params[:clean]
      FileUtils.rm_rf(output_path)
    else
      UI.user_error!("output path was exists: #{File.expand_path(output_path)}。 \nyou can use `clean:true` to force clean.")
    end
  end

  FileUtils.mkdir_p(output_path)
  output_path
end
find_file(default_file) click to toggle source
# File lib/fastlane/plugin/android_channels/helper/android_channels_helper.rb, line 163
def self.find_file(default_file)
  return default_file if File.file?(default_file)

  Dir['**/*'].each do |file|
    return file if File.basename(file) == default_file
  end

  nil
end
is_signed?(apk_file) click to toggle source

验证 apk 是否签名

# File lib/fastlane/plugin/android_channels/helper/android_channels_helper.rb, line 71
def self.is_signed?(apk_file)
  Zip.warn_invalid_date = false
  Zip::File.open(apk_file, Zip::File::CREATE) do |zip_file|
    file = zip_file.find_entry("META-INF/MANIFEST.MF")
    if file
      encrypt_keys = 0
      file.get_input_stream do |io|
        io.each_line do |line|
          return true if encrypt_keys >= 2
          encrypt_keys += 1 if line.start_with?("Name:")
        end
      end
    end
  end

  false
end
load_channel_file(file) click to toggle source
# File lib/fastlane/plugin/android_channels/helper/android_channels_helper.rb, line 173
def self.load_channel_file(file)
  content = File.read(file)
  case File.extname(file)
  when ".json"
    JSON.load(content).to_a
  when ".yaml", ".yml"
    YAML.load(content).to_a
  else
    # 安装纯文本解析
    content.gsub(/(\s|\n)+/, ",").split(",").select {|n| n && !n.empty?}
  end
end
packing_apk(apk_file, channels, output_path, options = {}) click to toggle source

签名接收的各渠道包

# File lib/fastlane/plugin/android_channels/helper/android_channels_helper.rb, line 21
def self.packing_apk(apk_file, channels, output_path, options = {})
  Zip.warn_invalid_date = false

  UI.message "Packaing apk ..."
  Tempfile.open("android_channel_name_file") do |empty_channel_file|
    channels.each do |channel_name|
      signed_and_write_channel_to_apk(apk_file, channel_name, output_path, empty_channel_file.path, options)
    end
  end
end
sign_apk(apk_file, options = {}) click to toggle source

签名 apk 文件

# File lib/fastlane/plugin/android_channels/helper/android_channels_helper.rb, line 54
def self.sign_apk(apk_file, options = {})
  command = "#{options[:apksigner].shellescape} sign #{options[:apksigner_args]} #{apk_file}"
  Action.sh(command, print_command: false, print_command_output: verbose?)
end
signed_and_write_channel_to_apk(apk_file, channel_name, output_path, write_file, options) click to toggle source

写入渠道并签名 apk 文件

# File lib/fastlane/plugin/android_channels/helper/android_channels_helper.rb, line 33
def self.signed_and_write_channel_to_apk(apk_file, channel_name, output_path, write_file, options)
  output_file = File.join(output_path, "#{channel_name}.apk")
  Action.sh("cp #{apk_file} #{output_file}", print_command: false)

  channel_filename = [options[:prefix], channel_name, options[:suffix]].compact.join("")
  UI.verbose "Writing 'META-INF/#{channel_filename}' file to #{output_file}"
  Zip::File.open(output_file, Zip::File::CREATE) do |zip_file|
    zip_file.add("META-INF/#{channel_filename}", write_file)
  end

  UI.verbose "Signing ..."
  sign_apk(output_file, options)

  if options[:verify] && !verify_apk(output_file, options[:apksigner])
    UI.build_failure! "Verify failure apk file: #{output_file}"
  end
rescue Zip::EntryExistsError => ex
  UI.build_failure!([ex.message].concat(ex.backtrace).join("\n"))
end
verbose?() click to toggle source
# File lib/fastlane/plugin/android_channels/helper/android_channels_helper.rb, line 89
def self.verbose?
  FastlaneCore::Globals.verbose?
end
verify_apk(apk_file, apksigner) click to toggle source

验证 apk 签名是否正确

# File lib/fastlane/plugin/android_channels/helper/android_channels_helper.rb, line 60
def self.verify_apk(apk_file, apksigner)
  command_args = [apksigner.shellescape, "verify"]
  command_args << "--verbose" if verbose?
  command_args << apk_file

  UI.verbose "Verifing ..."
  result = Action.sh(command_args.join(" "), print_command: false, print_command_output: verbose?)
  !result.include?("DOES NOT VERIFY")
end