fir-cli-x 1.7.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.codeclimate.yml +8 -0
- data/.dockerignore +2 -0
- data/.flow-plugin.yml +14 -0
- data/.gitignore +27 -0
- data/.travis.yml +23 -0
- data/CHANGELOG +194 -0
- data/Dockerfile +12 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +22 -0
- data/README.md +63 -0
- data/Rakefile +10 -0
- data/bin/console +11 -0
- data/bin/fir +14 -0
- data/bin/setup +7 -0
- data/doc/help.md +34 -0
- data/doc/info.md +44 -0
- data/doc/install.md +67 -0
- data/doc/login.md +19 -0
- data/doc/publish.md +35 -0
- data/doc/upgrade.md +7 -0
- data/fir-cli.gemspec +52 -0
- data/fir.sh +46 -0
- data/install.sh +210 -0
- data/lib/fir-cli.rb +3 -0
- data/lib/fir.rb +28 -0
- data/lib/fir/api.yml +7 -0
- data/lib/fir/cli.rb +181 -0
- data/lib/fir/patches.rb +10 -0
- data/lib/fir/patches/blank.rb +131 -0
- data/lib/fir/patches/concern.rb +146 -0
- data/lib/fir/patches/default_headers.rb +9 -0
- data/lib/fir/patches/hash.rb +79 -0
- data/lib/fir/patches/instance_variables.rb +30 -0
- data/lib/fir/patches/native_patch.rb +28 -0
- data/lib/fir/patches/os_patch.rb +28 -0
- data/lib/fir/patches/try.rb +102 -0
- data/lib/fir/util.rb +86 -0
- data/lib/fir/util/build_apk.rb +77 -0
- data/lib/fir/util/build_common.rb +93 -0
- data/lib/fir/util/build_ipa.rb +11 -0
- data/lib/fir/util/config.rb +43 -0
- data/lib/fir/util/http.rb +23 -0
- data/lib/fir/util/info.rb +38 -0
- data/lib/fir/util/login.rb +17 -0
- data/lib/fir/util/mapping.rb +98 -0
- data/lib/fir/util/me.rb +19 -0
- data/lib/fir/util/parser/apk.rb +46 -0
- data/lib/fir/util/parser/bin/pngcrush +0 -0
- data/lib/fir/util/parser/common.rb +24 -0
- data/lib/fir/util/parser/ipa.rb +188 -0
- data/lib/fir/util/parser/pngcrush.rb +23 -0
- data/lib/fir/util/publish.rb +253 -0
- data/lib/fir/version.rb +5 -0
- data/lib/fir/xcode_wrapper.sh +29 -0
- data/lib/fir_cli.rb +3 -0
- data/test/build_ipa_test.rb +17 -0
- data/test/cases/test_apk.apk +0 -0
- data/test/cases/test_apk_txt +1 -0
- data/test/cases/test_ipa.ipa +0 -0
- data/test/cases/test_ipa_dsym +0 -0
- data/test/info_test.rb +36 -0
- data/test/login_test.rb +12 -0
- data/test/mapping_test.rb +18 -0
- data/test/me_test.rb +17 -0
- data/test/publish_test.rb +44 -0
- data/test/test_helper.rb +98 -0
- metadata +273 -0
@@ -0,0 +1,93 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module FIR
|
4
|
+
module BuildCommon
|
5
|
+
|
6
|
+
def initialize_build_common_options(args, options)
|
7
|
+
@build_dir = initialize_build_dir(args, options)
|
8
|
+
@output_path = initialize_output_path(options)
|
9
|
+
@token = options[:token] || current_token
|
10
|
+
@changelog = options[:changelog].to_s
|
11
|
+
@short = options[:short].to_s
|
12
|
+
@name = options[:name].to_s
|
13
|
+
@proj = options[:proj].to_s
|
14
|
+
@export_qrcode = options[:qrcode]
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize_build_dir(args, options)
|
18
|
+
build_dir = args.first.to_s
|
19
|
+
if File.extname(build_dir) == '.git'
|
20
|
+
args.shift && git_clone_build_dir(build_dir, options)
|
21
|
+
elsif build_dir.blank? || !File.exist?(build_dir)
|
22
|
+
Dir.pwd
|
23
|
+
else
|
24
|
+
args.shift && File.absolute_path(build_dir)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def git_clone_build_dir(ssh_url, options)
|
29
|
+
repo_name = File.basename(ssh_url, '.git') + "_#{Time.now.strftime('%Y%m%dT%H%M%S')}"
|
30
|
+
branch = options[:branch].blank? ? 'master' : options[:branch]
|
31
|
+
git_cmd = "git clone --depth=50 --branch=#{branch} #{ssh_url} #{repo_name}"
|
32
|
+
|
33
|
+
logger.info git_cmd
|
34
|
+
logger_info_dividing_line
|
35
|
+
|
36
|
+
if system(git_cmd)
|
37
|
+
File.absolute_path(repo_name)
|
38
|
+
else
|
39
|
+
logger.error 'Git clone failed'
|
40
|
+
exit 1
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def initialize_output_path(options)
|
45
|
+
if options[:output].blank?
|
46
|
+
output_path = "#{@build_dir}/fir_build"
|
47
|
+
FileUtils.mkdir_p(output_path) unless File.exist?(output_path)
|
48
|
+
output_path
|
49
|
+
else
|
50
|
+
output_path = options[:output].to_s
|
51
|
+
unless File.exist?(output_path)
|
52
|
+
logger.warn "The output path not exist and fir-cli will autocreate it..."
|
53
|
+
end
|
54
|
+
File.absolute_path(output_path)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def publish_build_app(options)
|
59
|
+
logger_info_blank_line
|
60
|
+
publish @builded_app_path, options
|
61
|
+
end
|
62
|
+
|
63
|
+
def logger_info_and_run_build_command
|
64
|
+
puts @build_cmd if $DEBUG
|
65
|
+
|
66
|
+
logger.info 'Building......'
|
67
|
+
logger_info_dividing_line
|
68
|
+
|
69
|
+
logger.info `#{@build_cmd}`
|
70
|
+
|
71
|
+
if $?.to_i != 0
|
72
|
+
logger.error 'Build failed'
|
73
|
+
exit 1
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# split ['a=1', 'b=2'] => { 'a' => '1', 'b' => '2' }
|
78
|
+
def split_assignment_array_to_hash(arr)
|
79
|
+
hash = {}
|
80
|
+
arr.each do |assignment|
|
81
|
+
k, v = assignment.split('=', 2).map(&:strip)
|
82
|
+
hash[k] = v
|
83
|
+
end
|
84
|
+
|
85
|
+
hash
|
86
|
+
end
|
87
|
+
|
88
|
+
# convert { "a" => "1", "b" => "2" } => "a='1' b='2'"
|
89
|
+
def convert_hash_to_assignment_string(hash)
|
90
|
+
hash.collect { |k, v| "#{k}='#{v}'" }.join(' ')
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module FIR
|
4
|
+
module Config
|
5
|
+
CONFIG_PATH = "#{ENV['HOME']}/.fir-cli"
|
6
|
+
APP_INFO_PATH = "#{ENV['HOME']}/.fir-cli-app"
|
7
|
+
API_YML_PATH = ENV['API_YML_PATH'] || File.expand_path('../../', __FILE__) + '/api.yml'
|
8
|
+
XCODE_WRAPPER_PATH = File.expand_path('../../', __FILE__) + '/xcode_wrapper.sh'
|
9
|
+
APP_FILE_TYPE = %w(.ipa .apk).freeze
|
10
|
+
|
11
|
+
def fir_api
|
12
|
+
@fir_api ||= YAML.load_file(API_YML_PATH).deep_symbolize_keys[:fir]
|
13
|
+
end
|
14
|
+
|
15
|
+
def bughd_api
|
16
|
+
@bughd_api ||= YAML.load_file(API_YML_PATH).deep_symbolize_keys[:bughd]
|
17
|
+
end
|
18
|
+
|
19
|
+
def config
|
20
|
+
return unless File.exist?(CONFIG_PATH)
|
21
|
+
@config ||= YAML.load_file(CONFIG_PATH).deep_symbolize_keys
|
22
|
+
end
|
23
|
+
|
24
|
+
def reload_config
|
25
|
+
@config = YAML.load_file(CONFIG_PATH).deep_symbolize_keys
|
26
|
+
end
|
27
|
+
|
28
|
+
def write_config(hash)
|
29
|
+
File.open(CONFIG_PATH, 'w+') { |f| f << YAML.dump(hash) }
|
30
|
+
end
|
31
|
+
|
32
|
+
def write_app_info(hash)
|
33
|
+
File.open(APP_INFO_PATH, 'w+') { |f| f << YAML.dump(hash) }
|
34
|
+
end
|
35
|
+
|
36
|
+
def current_token
|
37
|
+
return @token = ENV["API_TOKEN"] if ENV["API_TOKEN"]
|
38
|
+
@token ||= config[:token] if config
|
39
|
+
end
|
40
|
+
|
41
|
+
alias_method :☠, :exit
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'api_tools'
|
5
|
+
|
6
|
+
module FIR
|
7
|
+
module Http
|
8
|
+
include ApiTools::DefaultRestModule
|
9
|
+
|
10
|
+
alias old_default_options default_options
|
11
|
+
def default_options
|
12
|
+
@default_options = old_default_options.merge(timeout: 300)
|
13
|
+
if ENV['FIR_TIMEOUT']
|
14
|
+
@default_options[:timeout] = ENV['FIR_TIMEOUT'].to_i
|
15
|
+
end
|
16
|
+
unless ENV['UPLOAD_VERIFY_SSL']
|
17
|
+
@default_options.merge!(other_base_execute_option: {
|
18
|
+
verify_ssl: OpenSSL::SSL::VERIFY_NONE
|
19
|
+
})
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module FIR
|
4
|
+
module Info
|
5
|
+
|
6
|
+
def info(*args, options)
|
7
|
+
file_path = File.absolute_path(args.first.to_s)
|
8
|
+
is_all = !options[:all].blank?
|
9
|
+
|
10
|
+
check_file_exist file_path
|
11
|
+
check_supported_file file_path
|
12
|
+
|
13
|
+
file_type = File.extname(file_path).delete('.')
|
14
|
+
|
15
|
+
logger.info "Analyzing #{file_type} file......"
|
16
|
+
logger_info_dividing_line
|
17
|
+
|
18
|
+
app_info = send("#{file_type}_info", file_path, full_info: is_all)
|
19
|
+
app_info.each { |k, v| logger.info "#{k}: #{v}" }
|
20
|
+
|
21
|
+
logger_info_blank_line
|
22
|
+
end
|
23
|
+
|
24
|
+
def ipa_info(ipa_path, options = {})
|
25
|
+
ipa = FIR::Parser::Ipa.new(ipa_path)
|
26
|
+
app = ipa.app
|
27
|
+
info = app.full_info(options)
|
28
|
+
ipa.cleanup
|
29
|
+
info
|
30
|
+
end
|
31
|
+
|
32
|
+
def apk_info(apk_path, options = {})
|
33
|
+
apk = FIR::Parser::Apk.new(apk_path)
|
34
|
+
info = apk.full_info(options)
|
35
|
+
info
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module FIR
|
4
|
+
module Login
|
5
|
+
def login(token)
|
6
|
+
check_token_cannot_be_blank token
|
7
|
+
|
8
|
+
user_info = fetch_user_info(token)
|
9
|
+
|
10
|
+
logger.info "Login succeed, previous user's email: #{config[:email]}" unless config.blank?
|
11
|
+
write_config(email: user_info.fetch(:email, ''), token: token)
|
12
|
+
reload_config
|
13
|
+
logger.info "Login succeed, current user's email: #{config[:email]}"
|
14
|
+
logger_info_blank_line
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module FIR
|
4
|
+
module Mapping
|
5
|
+
|
6
|
+
def mapping(*args, options)
|
7
|
+
initialize_and_check_mapping_options(args, options)
|
8
|
+
check_file_and_token
|
9
|
+
|
10
|
+
logger.info "Creating bughd project's version......."
|
11
|
+
logger_info_dividing_line
|
12
|
+
|
13
|
+
@full_version = find_or_create_bughd_full_version
|
14
|
+
|
15
|
+
logger.info 'Uploading mapping file.......'
|
16
|
+
|
17
|
+
upload_mapping_file
|
18
|
+
logger_info_dividing_line
|
19
|
+
|
20
|
+
logger.info "Uploaded succeed: #{bughd_api[:domain]}/project/#{@proj}/settings"
|
21
|
+
logger_info_blank_line
|
22
|
+
end
|
23
|
+
|
24
|
+
def find_or_create_bughd_full_version
|
25
|
+
url = bughd_api[:project_url] + "/#{@proj}/full_versions"
|
26
|
+
post url, version: @version, build: @build, uuid: uuid
|
27
|
+
end
|
28
|
+
|
29
|
+
def upload_mapping_file
|
30
|
+
tmp_file_path = generate_temp_mapping_file
|
31
|
+
|
32
|
+
url = bughd_api[:full_version_url] + "/#{@full_version[:id]}"
|
33
|
+
patch url, file: File.new(tmp_file_path, 'rb'), project_id: @proj, uuid: uuid
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def initialize_and_check_mapping_options(args, options)
|
39
|
+
@file_path = File.absolute_path(args.first.to_s)
|
40
|
+
@token = options[:token] || current_token
|
41
|
+
@proj = options[:proj].to_s
|
42
|
+
@version = options[:version].to_s
|
43
|
+
@build = options[:build].to_s
|
44
|
+
end
|
45
|
+
|
46
|
+
def check_file_and_token
|
47
|
+
check_file_exist(@file_path)
|
48
|
+
check_token_cannot_be_blank(@token)
|
49
|
+
check_project_id_cannot_be_blank
|
50
|
+
end
|
51
|
+
|
52
|
+
def check_project_id_cannot_be_blank
|
53
|
+
return unless @proj.blank?
|
54
|
+
|
55
|
+
logger.error "Project id can't be blank"
|
56
|
+
exit 1
|
57
|
+
end
|
58
|
+
|
59
|
+
def uuid
|
60
|
+
@uuid ||= fetch_user_uuid(@token)
|
61
|
+
end
|
62
|
+
|
63
|
+
def generate_temp_mapping_file
|
64
|
+
tmp_file_path = "#{Dir.tmpdir}/fircli-#{File.basename(@file_path)}"
|
65
|
+
FileUtils.cp(@file_path, tmp_file_path)
|
66
|
+
|
67
|
+
tmp_file_path = zip_mapping_file(tmp_file_path)
|
68
|
+
tmp_file_path = dsym_or_txt_file(tmp_file_path)
|
69
|
+
|
70
|
+
tmp_file_path
|
71
|
+
end
|
72
|
+
|
73
|
+
def zip_mapping_file(tmp_file_path)
|
74
|
+
if File.size?(tmp_file_path) > 50 * 1000 * 1000
|
75
|
+
logger.info 'Zipping mapping file.......'
|
76
|
+
|
77
|
+
system("zip -qr #{tmp_file_path}.zip #{tmp_file_path}")
|
78
|
+
tmp_file_path += '.zip'
|
79
|
+
|
80
|
+
logger.info "Zipped Mapping file size - #{File.size?(tmp_file_path)}"
|
81
|
+
end
|
82
|
+
|
83
|
+
tmp_file_path
|
84
|
+
end
|
85
|
+
|
86
|
+
def dsym_or_txt_file(tmp_file_path)
|
87
|
+
if File.dsym?(@file_path)
|
88
|
+
FileUtils.mv(tmp_file_path, tmp_file_path + '.dSYM')
|
89
|
+
tmp_file_path += '.dSYM'
|
90
|
+
elsif File.text?(@file_path)
|
91
|
+
FileUtils.mv(tmp_file_path, tmp_file_path + '.txt')
|
92
|
+
tmp_file_path += '.txt'
|
93
|
+
end
|
94
|
+
|
95
|
+
tmp_file_path
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
data/lib/fir/util/me.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module FIR
|
4
|
+
module Me
|
5
|
+
|
6
|
+
def me
|
7
|
+
check_logined
|
8
|
+
|
9
|
+
user_info = fetch_user_info(current_token)
|
10
|
+
|
11
|
+
email = user_info.fetch(:email, '')
|
12
|
+
name = user_info.fetch(:name, '')
|
13
|
+
|
14
|
+
logger.info "Login succeed, current user's email: #{email}"
|
15
|
+
logger.info "Login succeed, current user's name: #{name}"
|
16
|
+
logger_info_blank_line
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './common'
|
4
|
+
module FIR
|
5
|
+
module Parser
|
6
|
+
class Apk
|
7
|
+
include Parser::Common
|
8
|
+
|
9
|
+
def initialize(path)
|
10
|
+
Zip.warn_invalid_date = false
|
11
|
+
@apk = ::Android::Apk.new(path)
|
12
|
+
end
|
13
|
+
|
14
|
+
def full_info(options)
|
15
|
+
basic_info[:icons] = tmp_icons if options.fetch(:full_info, false)
|
16
|
+
|
17
|
+
basic_info
|
18
|
+
end
|
19
|
+
|
20
|
+
def basic_info
|
21
|
+
@basic_info ||= {
|
22
|
+
type: 'android',
|
23
|
+
name: fetch_label,
|
24
|
+
identifier: @apk.manifest.package_name,
|
25
|
+
build: @apk.manifest.version_code.to_s,
|
26
|
+
version: @apk.manifest.version_name.to_s
|
27
|
+
}
|
28
|
+
@basic_info.reject! { |_k, v| v.nil? }
|
29
|
+
@basic_info
|
30
|
+
end
|
31
|
+
|
32
|
+
# @apk.icon is a hash, { icon_name: icon_binary_data }
|
33
|
+
def tmp_icons
|
34
|
+
@apk.icon.map { |_, data| generate_tmp_icon(data, :apk) }
|
35
|
+
rescue StandardError
|
36
|
+
[]
|
37
|
+
end
|
38
|
+
|
39
|
+
def fetch_label
|
40
|
+
@apk.label
|
41
|
+
rescue NoMethodError
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
Binary file
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module FIR
|
4
|
+
module Parser
|
5
|
+
module Common
|
6
|
+
|
7
|
+
# when type is ipa, the icon data is a png file.
|
8
|
+
# when type is apk, the icon data is a binary data.
|
9
|
+
def generate_tmp_icon data, type
|
10
|
+
tmp_icon_path = "#{Dir.tmpdir}/icon-#{SecureRandom.hex[4..9]}.png"
|
11
|
+
|
12
|
+
if type == :ipa
|
13
|
+
FileUtils.cp(data, tmp_icon_path)
|
14
|
+
elsif type == :apk
|
15
|
+
File.open(tmp_icon_path, 'w+') { |f| f << data }
|
16
|
+
else
|
17
|
+
return
|
18
|
+
end
|
19
|
+
|
20
|
+
tmp_icon_path
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require_relative './common'
|
4
|
+
|
5
|
+
module FIR
|
6
|
+
module Parser
|
7
|
+
class Ipa
|
8
|
+
include Parser::Common
|
9
|
+
|
10
|
+
def initialize(path)
|
11
|
+
@path = path
|
12
|
+
end
|
13
|
+
|
14
|
+
def app
|
15
|
+
@app ||= App.new(app_path, is_stored)
|
16
|
+
end
|
17
|
+
|
18
|
+
def app_path
|
19
|
+
@app_path ||= Dir.glob(File.join(contents, 'Payload', '*.app')).first
|
20
|
+
end
|
21
|
+
|
22
|
+
def cleanup
|
23
|
+
return unless @contents
|
24
|
+
FileUtils.rm_rf(@contents)
|
25
|
+
@contents = nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def metadata
|
29
|
+
return unless has_metadata?
|
30
|
+
@metadata ||= CFPropertyList.native_types(CFPropertyList::List.new(file: metadata_path).value)
|
31
|
+
end
|
32
|
+
|
33
|
+
def has_metadata?
|
34
|
+
File.file?(metadata_path)
|
35
|
+
end
|
36
|
+
|
37
|
+
def metadata_path
|
38
|
+
@metadata_path ||= File.join(@contents, 'iTunesMetadata.plist')
|
39
|
+
end
|
40
|
+
|
41
|
+
def is_stored
|
42
|
+
has_metadata? ? true : false
|
43
|
+
end
|
44
|
+
|
45
|
+
def contents
|
46
|
+
return if @contents
|
47
|
+
@contents = "#{Dir.tmpdir}/ipa_files-#{Time.now.to_i}"
|
48
|
+
|
49
|
+
Zip::File.open(@path) do |zip_file|
|
50
|
+
zip_file.each do |f|
|
51
|
+
f_path = File.join(@contents, f.name)
|
52
|
+
FileUtils.mkdir_p(File.dirname(f_path))
|
53
|
+
zip_file.extract(f, f_path) unless File.exist?(f_path)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
@contents
|
58
|
+
end
|
59
|
+
|
60
|
+
class App
|
61
|
+
include Parser::Common
|
62
|
+
|
63
|
+
def initialize(path, is_stored = false)
|
64
|
+
@path = path
|
65
|
+
@is_stored = is_stored
|
66
|
+
end
|
67
|
+
|
68
|
+
def full_info(options)
|
69
|
+
if options.fetch(:full_info, false)
|
70
|
+
basic_info.merge!(icons: tmp_icons)
|
71
|
+
end
|
72
|
+
|
73
|
+
basic_info
|
74
|
+
end
|
75
|
+
|
76
|
+
def basic_info
|
77
|
+
@basic_info ||= {
|
78
|
+
type: 'ios',
|
79
|
+
identifier: identifier,
|
80
|
+
name: name,
|
81
|
+
display_name: display_name,
|
82
|
+
build: version.to_s,
|
83
|
+
version: short_version.to_s,
|
84
|
+
devices: devices,
|
85
|
+
release_type: release_type,
|
86
|
+
distribution_name: distribution_name
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
90
|
+
def info
|
91
|
+
@info ||= CFPropertyList.native_types(
|
92
|
+
CFPropertyList::List.new(file: File.join(@path, 'Info.plist')).value)
|
93
|
+
end
|
94
|
+
|
95
|
+
def name
|
96
|
+
info['CFBundleName']
|
97
|
+
end
|
98
|
+
|
99
|
+
def identifier
|
100
|
+
info['CFBundleIdentifier']
|
101
|
+
end
|
102
|
+
|
103
|
+
def display_name
|
104
|
+
info['CFBundleDisplayName']
|
105
|
+
end
|
106
|
+
|
107
|
+
def version
|
108
|
+
info['CFBundleVersion']
|
109
|
+
end
|
110
|
+
|
111
|
+
def short_version
|
112
|
+
info['CFBundleShortVersionString']
|
113
|
+
end
|
114
|
+
|
115
|
+
def tmp_icons
|
116
|
+
icons.map { |data| generate_tmp_icon(data, :ipa) }
|
117
|
+
end
|
118
|
+
|
119
|
+
def icons
|
120
|
+
@icons ||= begin
|
121
|
+
icons = []
|
122
|
+
info['CFBundleIcons']['CFBundlePrimaryIcon']['CFBundleIconFiles'].each do |name|
|
123
|
+
icons << get_image(name)
|
124
|
+
icons << get_image("#{name}@2x")
|
125
|
+
end
|
126
|
+
icons.delete_if &:!
|
127
|
+
rescue NoMethodError
|
128
|
+
[]
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def mobileprovision
|
133
|
+
return unless has_mobileprovision?
|
134
|
+
return @mobileprovision if @mobileprovision
|
135
|
+
|
136
|
+
cmd = "security cms -D -i \"#{mobileprovision_path}\""
|
137
|
+
begin
|
138
|
+
@mobileprovision = CFPropertyList.native_types(CFPropertyList::List.new(data: `#{cmd}`).value)
|
139
|
+
rescue CFFormatError
|
140
|
+
@mobileprovision = {}
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def has_mobileprovision?
|
145
|
+
File.file? mobileprovision_path
|
146
|
+
end
|
147
|
+
|
148
|
+
def mobileprovision_path
|
149
|
+
@mobileprovision_path ||= File.join(@path, 'embedded.mobileprovision')
|
150
|
+
end
|
151
|
+
|
152
|
+
def hide_developer_certificates
|
153
|
+
mobileprovision.delete('DeveloperCertificates') if has_mobileprovision?
|
154
|
+
end
|
155
|
+
|
156
|
+
def devices
|
157
|
+
mobileprovision['ProvisionedDevices'] if has_mobileprovision?
|
158
|
+
end
|
159
|
+
|
160
|
+
def distribution_name
|
161
|
+
"#{mobileprovision['Name']} - #{mobileprovision['TeamName']}" if has_mobileprovision?
|
162
|
+
end
|
163
|
+
|
164
|
+
def release_type
|
165
|
+
if @is_stored
|
166
|
+
'store'
|
167
|
+
else
|
168
|
+
if has_mobileprovision?
|
169
|
+
if devices
|
170
|
+
'adhoc'
|
171
|
+
else
|
172
|
+
'inhouse'
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
private
|
179
|
+
|
180
|
+
def get_image(name)
|
181
|
+
path = File.join(@path, "#{name}.png")
|
182
|
+
return nil unless File.exist?(path)
|
183
|
+
path
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|