Merge branch 'develop'

This commit is contained in:
NaixSpirit
2015-07-30 15:57:37 +08:00
16 changed files with 534 additions and 354 deletions

1
.gitignore vendored
View File

@@ -8,6 +8,7 @@
/spec/reports/
/tmp/
/fir-cli_tmp/
/test/build_project/
*.bundle
*.so
*.o

View File

@@ -23,106 +23,5 @@ require 'fir/version'
require 'fir/cli'
module FIR
CONFIG_PATH = "#{ENV['HOME']}/.fir-cli"
API_YML_PATH = "#{File.dirname(__FILE__)}/fir/api.yml"
APP_FILE_TYPE = %w(.ipa .apk).freeze
include Util
class << self
attr_accessor :logger, :api, :config
def api
@api ||= YAML.load_file(API_YML_PATH).symbolize_keys
end
def config
@config ||= YAML.load_file(CONFIG_PATH).symbolize_keys if File.exist?(CONFIG_PATH)
end
def reload_config
@config = YAML.load_file(CONFIG_PATH).symbolize_keys
end
def write_config hash
File.open(CONFIG_PATH, 'w+') { |f| f << YAML.dump(hash) }
end
def current_token
@token ||= config[:token] if config
end
def get url, params = {}, timeout = 300
begin
res = ::RestClient::Request.execute(
method: :get,
url: url,
timeout: timeout,
headers: default_headers.merge(params: params)
)
rescue => e
logger.error "#{e.class}\n#{e.message}"
exit 1
end
JSON.parse(res.body.force_encoding("UTF-8"), symbolize_names: true)
end
def post url, query, timeout = 300
begin
res = ::RestClient::Request.execute(
method: :post,
url: url,
payload: query,
timeout: timeout,
headers: default_headers
)
rescue => e
logger.error "#{e.class}\n#{e.message}"
exit 1
end
JSON.parse(res.body.force_encoding("UTF-8"), symbolize_names: true)
end
def patch url, query, timeout = 300
begin
res = ::RestClient::Request.execute(
method: :patch,
url: url,
payload: query,
timeout: timeout,
headers: default_headers
)
rescue => e
logger.error "#{e.class}\n#{e.message}"
exit 1
end
JSON.parse(res.body.force_encoding("UTF-8"), symbolize_names: true)
end
def put url, query, timeout = 300
begin
res = ::RestClient::Request.execute(
method: :put,
url: url,
payload: query,
timeout: timeout,
headers: default_headers
)
rescue => e
logger.error "#{e.class}\n#{e.message}"
exit 1
end
JSON.parse(res.body.force_encoding("UTF-8"), symbolize_names: true)
end
def default_headers
{ content_type: :json, source: 'fir-cli', cli_version: FIR::VERSION }
end
alias_method :, :exit
end
end

View File

@@ -1,5 +1,10 @@
domain: 'http://fir.im'
base_url: 'http://api.fir.im'
user_url: 'http://api.fir.im/user'
app_url: 'http://api.fir.im/apps'
udids_url: 'http://api.fir.im/devices/multi_udid'
fir:
domain: 'http://fir.im'
base_url: 'http://api.fir.im'
user_url: 'http://api.fir.im/user'
app_url: 'http://api.fir.im/apps'
udids_url: 'http://api.fir.im/devices/multi_udid'
bughd:
domain: 'http://bughd.com'
base_url: 'http://api.bughd.com'
project_url: 'http://api.bughd.com/projects'

View File

@@ -2,7 +2,7 @@
module FIR
class CLI < Thor
class_option :token, type: :string, aliases: "-T", desc: "User's api_token at FIR.im"
class_option :token, type: :string, aliases: "-T", desc: "User's API Token at FIR.im"
class_option :logfile, type: :string, aliases: "-L", desc: "Path to writable logfile"
class_option :verbose, type: :boolean, aliases: "-V", desc: "Show verbose", default: true
class_option :quiet, type: :boolean, aliases: "-q", desc: "Silence commands"
@@ -16,9 +16,9 @@ module FIR
Example:
$ fir b <project dir> [-C <configuration>] [-t <target name>] [-o <ipa output dir>] [settings] [-c <changelog>] [-p -T <your token>]
$ fir b <project dir> [-C <configuration>] [-t <target name>] [-o <ipa output dir>] [settings] [-c <changelog>] [-p -T <your api token>]
$ fir b <workspace dir> -w -S <scheme name> [-C <configuration>] [-t <target name>] [-o <ipa output dir>] [settings] [-c <changelog>] [-p -T <your token>]
$ fir b <workspace dir> -w -S <scheme name> [-C <configuration>] [-t <target name>] [-o <ipa output dir>] [settings] [-c <changelog>] [-p -T <your api token>]
LONGDESC
map ["b", "build"] => :build_ipa
method_option :workspace, type: :boolean, aliases: "-w", desc: "true/false if build workspace"
@@ -46,6 +46,13 @@ module FIR
end
desc "publish APP_FILE_PATH", "Publish iOS/Android app to FIR.im, support ipa/apk file (aliases: 'p')."
long_desc <<-LONGDESC
`publish` command will publish your app file to FIR.im, also the command support to publish app's short & changelog.
Example:
$ fir p <app file path> [-c <changelog> -s <custom short link>]
LONGDESC
map "p" => :publish
method_option :short, type: :string, aliases: "-s", desc: "Set custom short link"
method_option :changelog, type: :string, aliases: "-c", desc: "Set changelog"
@@ -60,7 +67,7 @@ module FIR
def login *args
prepare :login
token = options[:token] || args.first || ask("Please enter your FIR.im token:", :white, echo: true)
token = options[:token] || args.first || ask("Please enter your FIR.im API Token:", :white, echo: true)
FIR.login(token)
end

View File

@@ -1,5 +1,5 @@
# encoding: utf-8
require_relative './patches/concern'
require_relative './patches/native_patch'
require_relative './patches/os_patch'
require_relative './patches/parser_patch'

Binary file not shown.

144
lib/fir/patches/concern.rb Normal file
View File

@@ -0,0 +1,144 @@
# encoding: utf-8
module ActiveSupport
# A typical module looks like this:
#
# module M
# def self.included(base)
# base.extend ClassMethods
# base.class_eval do
# scope :disabled, -> { where(disabled: true) }
# end
# end
#
# module ClassMethods
# ...
# end
# end
#
# By using <tt>ActiveSupport::Concern</tt> the above module could instead be
# written as:
#
# require 'active_support/concern'
#
# module M
# extend ActiveSupport::Concern
#
# included do
# scope :disabled, -> { where(disabled: true) }
# end
#
# class_methods do
# ...
# end
# end
#
# Moreover, it gracefully handles module dependencies. Given a +Foo+ module
# and a +Bar+ module which depends on the former, we would typically write the
# following:
#
# module Foo
# def self.included(base)
# base.class_eval do
# def self.method_injected_by_foo
# ...
# end
# end
# end
# end
#
# module Bar
# def self.included(base)
# base.method_injected_by_foo
# end
# end
#
# class Host
# include Foo # We need to include this dependency for Bar
# include Bar # Bar is the module that Host really needs
# end
#
# But why should +Host+ care about +Bar+'s dependencies, namely +Foo+? We
# could try to hide these from +Host+ directly including +Foo+ in +Bar+:
#
# module Bar
# include Foo
# def self.included(base)
# base.method_injected_by_foo
# end
# end
#
# class Host
# include Bar
# end
#
# Unfortunately this won't work, since when +Foo+ is included, its <tt>base</tt>
# is the +Bar+ module, not the +Host+ class. With <tt>ActiveSupport::Concern</tt>,
# module dependencies are properly resolved:
#
# require 'active_support/concern'
#
# module Foo
# extend ActiveSupport::Concern
# included do
# def self.method_injected_by_foo
# ...
# end
# end
# end
#
# module Bar
# extend ActiveSupport::Concern
# include Foo
#
# included do
# self.method_injected_by_foo
# end
# end
#
# class Host
# include Bar # It works, now Bar takes care of its dependencies
# end
module Concern
class MultipleIncludedBlocks < StandardError #:nodoc:
def initialize
super "Cannot define multiple 'included' blocks for a Concern"
end
end
def self.extended(base) #:nodoc:
base.instance_variable_set(:@_dependencies, [])
end
def append_features(base)
if base.instance_variable_defined?(:@_dependencies)
base.instance_variable_get(:@_dependencies) << self
return false
else
return false if base < self
@_dependencies.each { |dep| base.send(:include, dep) }
super
base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods)
base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block)
end
end
def included(base = nil, &block)
if base.nil?
raise MultipleIncludedBlocks if instance_variable_defined?(:@_included_block)
@_included_block = block
else
super
end
end
def class_methods(&class_methods_module_definition)
mod = const_defined?(:ClassMethods, false) ?
const_get(:ClassMethods) :
const_set(:ClassMethods, Module.new)
mod.module_eval(&class_methods_module_definition)
end
end
end

View File

@@ -1,169 +0,0 @@
# encoding: utf-8
module Parser
class << self
def png_bin
@png_bin ||= File.expand_path("../bin/pngcrush", __FILE__)
end
def uncrush_icon crushed_icon_path, uncrushed_icon_path
system("#{png_bin} -revert-iphone-optimizations #{crushed_icon_path} #{uncrushed_icon_path} &> /dev/null")
end
def crush_icon uncrushed_icon_path, crushed_icon_path
system("#{png_bin} -iphone #{uncrushed_icon_path} #{crushed_icon_path} &> /dev/null")
end
end
class IPA
def initialize(path)
@path = path
end
def app
@app ||= App.new(app_path)
end
def app_path
@app_path ||= Dir.glob(File.join(contents, 'Payload', '*.app')).first
end
def cleanup
return unless @contents
FileUtils.rm_rf(@contents)
@contents = nil
end
def metadata
return unless has_metadata?
@metadata ||= CFPropertyList.native_types(CFPropertyList::List.new(file: metadata_path).value)
end
def has_metadata?
File.file? metadata_path
end
def metadata_path
@metadata_path ||= File.join(@contents, 'iTunesMetadata.plist')
end
def release_type
has_metadata? ? 'store' : 'adhoc'
end
private
def contents
return if @contents
@contents = "fir-cli_tmp/ipa_files-#{Time.now.to_i}"
Zip::File.open(@path) do |zip_file|
zip_file.each do |f|
f_path = File.join(@contents, f.name)
FileUtils.mkdir_p(File.dirname(f_path))
zip_file.extract(f, f_path) unless File.exist?(f_path)
end
end
@contents
end
end
class App
def initialize(path)
@path = path
end
def info
@info ||= CFPropertyList.native_types(
CFPropertyList::List.new(file: File.join(@path, 'Info.plist')).value)
end
def name
info['CFBundleName']
end
def identifier
info['CFBundleIdentifier']
end
def display_name
info['CFBundleDisplayName']
end
def version
info['CFBundleVersion']
end
def short_version
info['CFBundleShortVersionString']
end
def icons
@icons ||= begin
icons = []
info['CFBundleIcons']['CFBundlePrimaryIcon']['CFBundleIconFiles'].each do |name|
icons << get_image(name)
icons << get_image("#{name}@2x")
end
icons.delete_if { |i| !i }
rescue NoMethodError
[]
end
end
def mobileprovision
return unless has_mobileprovision?
return @mobileprovision if @mobileprovision
cmd = "security cms -D -i \"#{mobileprovision_path}\""
begin
@mobileprovision = CFPropertyList.native_types(CFPropertyList::List.new(data: `#{cmd}`).value)
rescue CFFormatError
@mobileprovision = {}
end
end
def has_mobileprovision?
File.file? mobileprovision_path
end
def mobileprovision_path
@mobileprovision_path ||= File.join(@path, 'embedded.mobileprovision')
end
def hide_developer_certificates
mobileprovision.delete('DeveloperCertificates') if has_mobileprovision?
end
def devices
mobileprovision['ProvisionedDevices'] if has_mobileprovision?
end
def distribution_name
"#{mobileprovision['Name']} - #{mobileprovision['TeamName']}" if has_mobileprovision?
end
def release_type
if has_mobileprovision?
if devices
'adhoc'
else
'inhouse'
end
end
end
private
def get_image name
path = File.join(@path, "#{name}.png")
return nil unless File.exist?(path)
path
end
end
end

View File

@@ -1,5 +1,8 @@
# encoding: utf-8
require_relative './util/http'
require_relative './util/config'
require_relative './util/parser'
require_relative './util/login'
require_relative './util/me'
require_relative './util/info'
@@ -8,20 +11,21 @@ require_relative './util/publish'
module FIR
module Util
def self.included base
base.extend ClassMethods
base.extend Login
base.extend Me
base.extend Info
base.extend Build
base.extend Publish
end
extend ActiveSupport::Concern
module ClassMethods
include FIR::Http
include FIR::Config
include FIR::Login
include FIR::Me
include FIR::Info
include FIR::Build
include FIR::Publish
attr_accessor :logger
def fetch_user_info token
get api[:user_url], api_token: token
get fir_api[:user_url], api_token: token
end
def check_supported_file path

View File

@@ -4,66 +4,27 @@ module FIR
module Build
def build_ipa *args, options
# initialize build options
# check build environment and make build cmd
check_osx
if args.first.blank? || !File.exist?(args.first)
@build_dir = Dir.pwd
else
@build_dir = File.absolute_path(args.shift.to_s) # pop the first param
end
@build_cmd = "xcodebuild build -sdk iphoneos"
@build_tmp_dir = Dir.mktmpdir
@custom_settings = parse_custom_settings(args) # convert ['a=1', 'b=2'] => { 'a' => '1', 'b' => '2' }
@configuration = options[:configuration]
@wrapper_name = File.basename(options[:name].to_s, '.*') + '.app' unless options[:name].blank?
@target_name = options[:target]
@scheme_name = options[:scheme]
@output_path = options[:output].blank? ? "#{@build_dir}/build_ipa" : File.absolute_path(options[:output].to_s)
@dsym_name = @wrapper_name + '.dSYM' unless @wrapper_name.blank?
@build_tmp_dir = Dir.mktmpdir
@output_path = options[:output].blank? ? "#{@build_dir}/fir_build_ipa" : File.absolute_path(options[:output].to_s)
@ipa_build_cmd = initialize_ipa_build_cmd(args, options)
# check build environment and make build cmd
check_osx
if options.workspace?
@workspace = check_and_find_workspace(@build_dir)
check_scheme(@scheme_name)
@build_cmd += " -workspace '#{@workspace}' -scheme '#{@scheme_name}'"
else
@project = check_and_find_project(@build_dir)
@build_cmd += " -project '#{@project}'"
end
@build_cmd += " -configuration '#{@configuration}'" unless @configuration.blank?
@build_cmd += " -target '#{@target_name}'" unless @target_name.blank?
# convert { "a" => "1", "b" => "2" } => "a='1' b='2'"
@setting_str = @custom_settings.collect { |k, v| "#{k}='#{v}'" }.join(' ')
@setting_str += " WRAPPER_NAME='#{@wrapper_name}'" unless @wrapper_name.blank?
@setting_str += " TARGET_BUILD_DIR='#{@build_tmp_dir}'" unless @custom_settings['TARGET_BUILD_DIR']
@setting_str += " CONFIGURATION_BUILD_DIR='#{@build_tmp_dir}'" unless @custom_settings['CONFIGURATION_BUILD_DIR']
@setting_str += " DWARF_DSYM_FOLDER_PATH='#{@output_path}'" unless @custom_settings['DWARF_DSYM_FOLDER_PATH']
@setting_str += " DWARF_DSYM_FILE_NAME='#{@dsym_name}'" unless @dsym_name.blank?
@build_cmd += " #{@setting_str} 2>&1"
puts @build_cmd if $DEBUG
puts @ipa_build_cmd if $DEBUG
logger.info "Building......"
logger_info_dividing_line
logger.info `#{@build_cmd}`
logger.info `#{@ipa_build_cmd}`
FileUtils.mkdir_p(@output_path) unless File.exist?(@output_path)
Dir.chdir(@build_tmp_dir) do
apps = Dir["*.app"]
if apps.length == 0
logger.error "Builded has no output app, Can not be packaged"
exit 1
end
apps.each do |app|
ipa_path = File.join(@output_path, "#{File.basename(app, '.app')}.ipa")
zip_app2ipa(File.join(@build_tmp_dir, app), ipa_path)
end
end
output_ipa
logger.info "Build Success"
@@ -78,12 +39,68 @@ module FIR
private
def parse_custom_settings args
def initialize_ipa_build_cmd args, options
ipa_build_cmd = "xcodebuild build -sdk iphoneos"
@configuration = options[:configuration]
@wrapper_name = File.basename(options[:name].to_s, '.*') + '.app' unless options[:name].blank?
@target_name = options[:target]
@scheme_name = options[:scheme]
@dsym_name = @wrapper_name + '.dSYM' unless @wrapper_name.blank?
if options.workspace?
workspace = check_and_find_ios_workspace(@build_dir)
check_ios_scheme(@scheme_name)
ipa_build_cmd += " -workspace '#{workspace}' -scheme '#{@scheme_name}'"
else
project = check_and_find_ios_project(@build_dir)
ipa_build_cmd += " -project '#{project}'"
end
ipa_build_cmd += " -configuration '#{@configuration}'" unless @configuration.blank?
ipa_build_cmd += " -target '#{@target_name}'" unless @target_name.blank?
ipa_build_cmd += " #{ipa_custom_settings(args)} 2>&1"
ipa_build_cmd
end
def ipa_custom_settings args
custom_settings = parse_ipa_custom_settings(args)
# convert { "a" => "1", "b" => "2" } => "a='1' b='2'"
setting_str = custom_settings.collect { |k, v| "#{k}='#{v}'" }.join(' ')
setting_str += " WRAPPER_NAME='#{@wrapper_name}'" unless @wrapper_name.blank?
setting_str += " TARGET_BUILD_DIR='#{@build_tmp_dir}'" unless custom_settings['TARGET_BUILD_DIR']
setting_str += " CONFIGURATION_BUILD_DIR='#{@build_tmp_dir}'" unless custom_settings['CONFIGURATION_BUILD_DIR']
setting_str += " DWARF_DSYM_FOLDER_PATH='#{@output_path}'" unless custom_settings['DWARF_DSYM_FOLDER_PATH']
setting_str += " DWARF_DSYM_FILE_NAME='#{@dsym_name}'" unless @dsym_name.blank?
setting_str
end
def output_ipa
FileUtils.mkdir_p(@output_path) unless File.exist?(@output_path)
Dir.chdir(@build_tmp_dir) do
apps = Dir["*.app"]
if apps.length == 0
logger.error "Builded has no output app, Can not be packaged"
exit 1
end
apps.each do |app|
ipa_path = File.join(@output_path, "#{File.basename(app, '.app')}.ipa")
zip_app2ipa(File.join(@build_tmp_dir, app), ipa_path)
end
end
end
# convert ['a=1', 'b=2'] => { 'a' => '1', 'b' => '2' }
def parse_ipa_custom_settings args
hash = {}
args.each do |setting|
k, v = setting.split('=', 2).map(&:strip)
hash[k] = v
end
hash
end
@@ -94,13 +111,13 @@ module FIR
end
end
def check_and_find_project path
def check_and_find_ios_project path
unless File.exist?(path)
logger.error "The first param BUILD_DIR must be a xcodeproj directory"
exit 1
end
if is_project?(path)
if is_ios_project?(path)
project = path
else
project = Dir["#{path}/*.xcodeproj"].first
@@ -113,13 +130,13 @@ module FIR
project
end
def check_and_find_workspace path
def check_and_find_ios_workspace path
unless File.exist?(path)
logger.error "The first param BUILD_DIR must be a xcworkspace directory"
exit 1
end
if is_workspace?(path)
if is_ios_workspace?(path)
workspace = path
else
workspace = Dir["#{path}/*.xcworkspace"].first
@@ -132,18 +149,18 @@ module FIR
workspace
end
def check_scheme scheme_name
def check_ios_scheme scheme_name
if scheme_name.blank?
logger.error "Must provide a scheme by `-S` option when build a workspace"
exit 1
end
end
def is_project? path
def is_ios_project? path
File.extname(path) == '.xcodeproj'
end
def is_workspace? path
def is_ios_workspace? path
File.extname(path) == '.xcworkspace'
end

35
lib/fir/util/config.rb Normal file
View File

@@ -0,0 +1,35 @@
# encoding: utf-8
module FIR
module Config
CONFIG_PATH = "#{ENV['HOME']}/.fir-cli"
API_YML_PATH = File.expand_path("../../", __FILE__) + '/api.yml'
APP_FILE_TYPE = %w(.ipa .apk).freeze
def fir_api
@fir_api ||= YAML.load_file(API_YML_PATH).deep_symbolize_keys[:fir]
end
def bughd_api
@bughd_api ||= YAML.load_file(API_YML_PATH).deep_symbolize_keys[:bughd]
end
def config
@config ||= YAML.load_file(CONFIG_PATH).deep_symbolize_keys if File.exist?(CONFIG_PATH)
end
def reload_config
@config = YAML.load_file(CONFIG_PATH).deep_symbolize_keys
end
def write_config hash
File.open(CONFIG_PATH, 'w+') { |f| f << YAML.dump(hash) }
end
def current_token
@token ||= config[:token] if config
end
alias_method :, :exit
end
end

79
lib/fir/util/http.rb Normal file
View File

@@ -0,0 +1,79 @@
# encoding: utf-8
module FIR
module Http
def get url, params = {}, timeout = 300
begin
res = ::RestClient::Request.execute(
method: :get,
url: url,
timeout: timeout,
headers: default_headers.merge(params: params)
)
rescue => e
logger.error "#{e.class}\n#{e.message}"
exit 1
end
JSON.parse(res.body.force_encoding("UTF-8"), symbolize_names: true)
end
def post url, query, timeout = 300
begin
res = ::RestClient::Request.execute(
method: :post,
url: url,
payload: query,
timeout: timeout,
headers: default_headers
)
rescue => e
logger.error "#{e.class}\n#{e.message}"
exit 1
end
JSON.parse(res.body.force_encoding("UTF-8"), symbolize_names: true)
end
def patch url, query, timeout = 300
begin
res = ::RestClient::Request.execute(
method: :patch,
url: url,
payload: query,
timeout: timeout,
headers: default_headers
)
rescue => e
logger.error "#{e.class}\n#{e.message}"
exit 1
end
JSON.parse(res.body.force_encoding("UTF-8"), symbolize_names: true)
end
def put url, query, timeout = 300
begin
res = ::RestClient::Request.execute(
method: :put,
url: url,
payload: query,
timeout: timeout,
headers: default_headers
)
rescue => e
logger.error "#{e.class}\n#{e.message}"
exit 1
end
JSON.parse(res.body.force_encoding("UTF-8"), symbolize_names: true)
end
private
def default_headers
{ content_type: :json, source: 'fir-cli', cli_version: FIR::VERSION }
end
end
end

View File

@@ -19,7 +19,7 @@ module FIR
end
def ipa_info ipa_path, is_all
ipa = Parser::IPA.new(ipa_path)
ipa = FIR::Parser::Ipa.new(ipa_path)
app = ipa.app
info = {

157
lib/fir/util/parser.rb Normal file
View File

@@ -0,0 +1,157 @@
# encoding: utf-8
module FIR
module Parser
class Ipa
def initialize(path)
@path = path
end
def app
@app ||= App.new(app_path)
end
def app_path
@app_path ||= Dir.glob(File.join(contents, 'Payload', '*.app')).first
end
def cleanup
return unless @contents
FileUtils.rm_rf(@contents)
@contents = nil
end
def metadata
return unless has_metadata?
@metadata ||= CFPropertyList.native_types(CFPropertyList::List.new(file: metadata_path).value)
end
def has_metadata?
File.file? metadata_path
end
def metadata_path
@metadata_path ||= File.join(@contents, 'iTunesMetadata.plist')
end
def release_type
has_metadata? ? 'store' : 'adhoc'
end
def contents
return if @contents
@contents = "fir-cli_tmp/ipa_files-#{Time.now.to_i}"
Zip::File.open(@path) do |zip_file|
zip_file.each do |f|
f_path = File.join(@contents, f.name)
FileUtils.mkdir_p(File.dirname(f_path))
zip_file.extract(f, f_path) unless File.exist?(f_path)
end
end
@contents
end
class App
def initialize(path)
@path = path
end
def info
@info ||= CFPropertyList.native_types(
CFPropertyList::List.new(file: File.join(@path, 'Info.plist')).value)
end
def name
info['CFBundleName']
end
def identifier
info['CFBundleIdentifier']
end
def display_name
info['CFBundleDisplayName']
end
def version
info['CFBundleVersion']
end
def short_version
info['CFBundleShortVersionString']
end
def icons
@icons ||= begin
icons = []
info['CFBundleIcons']['CFBundlePrimaryIcon']['CFBundleIconFiles'].each do |name|
icons << get_image(name)
icons << get_image("#{name}@2x")
end
icons.delete_if { |i| !i }
rescue NoMethodError
[]
end
end
def mobileprovision
return unless has_mobileprovision?
return @mobileprovision if @mobileprovision
cmd = "security cms -D -i \"#{mobileprovision_path}\""
begin
@mobileprovision = CFPropertyList.native_types(CFPropertyList::List.new(data: `#{cmd}`).value)
rescue CFFormatError
@mobileprovision = {}
end
end
def has_mobileprovision?
File.file? mobileprovision_path
end
def mobileprovision_path
@mobileprovision_path ||= File.join(@path, 'embedded.mobileprovision')
end
def hide_developer_certificates
mobileprovision.delete('DeveloperCertificates') if has_mobileprovision?
end
def devices
mobileprovision['ProvisionedDevices'] if has_mobileprovision?
end
def distribution_name
"#{mobileprovision['Name']} - #{mobileprovision['TeamName']}" if has_mobileprovision?
end
def release_type
if has_mobileprovision?
if devices
'adhoc'
else
'inhouse'
end
end
end
private
def get_image name
path = File.join(@path, "#{name}.png")
return nil unless File.exist?(path)
path
end
end
end
class Apk
end
end
end

View File

@@ -8,8 +8,8 @@ module FIR
token = options[:token] || current_token
changelog = options[:changelog].to_s
check_supported_file file_path
check_token_cannot_be_blank token
check_supported_file(file_path)
check_token_cannot_be_blank(token)
fetch_user_info(token)
logger.info "Publishing app......."
@@ -47,7 +47,7 @@ module FIR
published_app_info = fetch_app_info(app_id, api_token: token)
logger_info_dividing_line
logger.info "Published succeed: #{api[:domain]}/#{published_app_info[:short]}"
logger.info "Published succeed: #{fir_api[:domain]}/#{published_app_info[:short]}"
end
end
@@ -81,23 +81,23 @@ module FIR
def upload_device_info hash
logger.info "Updating devices info......"
post api[:udids_url], hash
post fir_api[:udids_url], hash
end
def update_app_info id, hash
logger.info "Updating app info......"
patch api[:app_url] + "/#{id}", hash
patch fir_api[:app_url] + "/#{id}", hash
end
def fetch_uploading_info hash
logger.info "Fetching #{@app_info[:identifier]}@FIR.im uploading info......"
post api[:app_url], hash
post fir_api[:app_url], hash
end
def fetch_app_info id, hash
logger.info "Fetch app info from FIR.im"
get api[:app_url] + "/#{id}", hash
get fir_api[:app_url] + "/#{id}", hash
end
end
end

View File

@@ -9,5 +9,6 @@ class PublishTest < Minitest::Test
}
assert FIR.publish(default_ipa, options)
assert FIR.publish(default_apk, options)
end
end