fir-cli 1.1.5 → 1.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.codeclimate.yml +8 -0
- data/.gitignore +1 -0
- data/CHANGELOG +10 -0
- data/README.md +43 -11
- data/Rakefile +3 -3
- data/fir-cli.gemspec +7 -3
- data/lib/fir/api.yml +11 -5
- data/lib/fir/cli.rb +39 -8
- data/lib/fir/patches/concern.rb +144 -0
- data/lib/fir/patches/native_patch.rb +19 -0
- data/lib/fir/patches.rb +1 -1
- data/lib/fir/util/build.rb +101 -60
- data/lib/fir/util/config.rb +35 -0
- data/lib/fir/util/http.rb +49 -0
- data/lib/fir/util/info.rb +6 -3
- data/lib/fir/util/login.rb +1 -0
- data/lib/fir/util/mapping.rb +65 -0
- data/lib/fir/util/me.rb +7 -6
- data/lib/fir/util/parser.rb +157 -0
- data/lib/fir/util/publish.rb +83 -64
- data/lib/fir/util.rb +34 -12
- data/lib/fir/version.rb +1 -1
- data/lib/fir.rb +0 -78
- data/test/build_ipa_test.rb +9 -0
- data/test/cases/test_apk.apk +0 -0
- data/test/cases/test_apk_txt +1 -0
- data/test/cases/test_ipa_dsym +0 -0
- data/test/info_test.rb +39 -0
- data/test/login_test.rb +12 -0
- data/test/mapping_test.rb +15 -0
- data/test/me_test.rb +17 -0
- data/test/publish_test.rb +14 -0
- data/test/test_helper.rb +49 -0
- metadata +48 -9
- data/lib/fir/patches/bin/pngcrush +0 -0
- data/lib/fir/patches/parser_patch.rb +0 -169
- data/test/fir_cli_test.rb +0 -88
data/lib/fir/util/build.rb
CHANGED
@@ -4,73 +4,37 @@ module FIR
|
|
4
4
|
module Build
|
5
5
|
|
6
6
|
def build_ipa *args, options
|
7
|
-
#
|
7
|
+
# check build environment and make build cmd
|
8
|
+
check_osx
|
9
|
+
|
8
10
|
if args.first.blank? || !File.exist?(args.first)
|
9
11
|
@build_dir = Dir.pwd
|
10
12
|
else
|
11
13
|
@build_dir = File.absolute_path(args.shift.to_s) # pop the first param
|
12
14
|
end
|
13
15
|
|
14
|
-
@
|
15
|
-
@
|
16
|
-
@
|
17
|
-
@
|
18
|
-
@wrapper_name = File.basename(options[:name].to_s, '.*') + '.app' unless options[:name].blank?
|
19
|
-
@target_name = options[:target]
|
20
|
-
@scheme_name = options[:scheme]
|
21
|
-
@output_path = options[:output].blank? ? "#{@build_dir}/build_ipa" : File.absolute_path(options[:output].to_s)
|
22
|
-
@dsym_name = @wrapper_name + '.dSYM' unless @wrapper_name.blank?
|
23
|
-
|
24
|
-
# check build environment and make build cmd
|
25
|
-
check_osx
|
26
|
-
if options.workspace?
|
27
|
-
@workspace = check_and_find_workspace(@build_dir)
|
28
|
-
check_scheme(@scheme_name)
|
29
|
-
@build_cmd += " -workspace '#{@workspace}' -scheme '#{@scheme_name}'"
|
30
|
-
else
|
31
|
-
@project = check_and_find_project(@build_dir)
|
32
|
-
@build_cmd += " -project '#{@project}'"
|
33
|
-
end
|
34
|
-
|
35
|
-
@build_cmd += " -configuration '#{@configuration}'" unless @configuration.blank?
|
36
|
-
@build_cmd += " -target '#{@target_name}'" unless @target_name.blank?
|
16
|
+
@token = options[:token] || current_token
|
17
|
+
@changelog = options[:changelog].to_s
|
18
|
+
@short = options[:short].to_s
|
19
|
+
@proj = options[:proj].to_s
|
37
20
|
|
38
|
-
|
39
|
-
@
|
40
|
-
@
|
41
|
-
@setting_str += " TARGET_BUILD_DIR='#{@build_tmp_dir}'" unless @custom_settings['TARGET_BUILD_DIR']
|
42
|
-
@setting_str += " CONFIGURATION_BUILD_DIR='#{@build_tmp_dir}'" unless @custom_settings['CONFIGURATION_BUILD_DIR']
|
43
|
-
@setting_str += " DWARF_DSYM_FOLDER_PATH='#{@output_path}'" unless @custom_settings['DWARF_DSYM_FOLDER_PATH']
|
44
|
-
@setting_str += " DWARF_DSYM_FILE_NAME='#{@dsym_name}'" unless @dsym_name.blank?
|
21
|
+
@build_tmp_dir = Dir.mktmpdir
|
22
|
+
@output_path = options[:output].blank? ? "#{@build_dir}/fir_build_ipa" : File.absolute_path(options[:output].to_s)
|
23
|
+
@ipa_build_cmd = initialize_ipa_build_cmd(args, options)
|
45
24
|
|
46
|
-
@
|
47
|
-
puts @build_cmd if $DEBUG
|
25
|
+
puts @ipa_build_cmd if $DEBUG
|
48
26
|
|
49
27
|
logger.info "Building......"
|
50
28
|
logger_info_dividing_line
|
51
29
|
|
52
|
-
logger.info `#{@
|
53
|
-
|
54
|
-
FileUtils.mkdir_p(@output_path) unless File.exist?(@output_path)
|
55
|
-
Dir.chdir(@build_tmp_dir) do
|
56
|
-
apps = Dir["*.app"]
|
57
|
-
if apps.length == 0
|
58
|
-
logger.error "Builded has no output app, Can not be packaged"
|
59
|
-
exit 1
|
60
|
-
end
|
30
|
+
logger.info `#{@ipa_build_cmd}`
|
61
31
|
|
62
|
-
|
63
|
-
ipa_path = File.join(@output_path, "#{File.basename(app, '.app')}.ipa")
|
64
|
-
zip_app2ipa(File.join(@build_tmp_dir, app), ipa_path)
|
65
|
-
end
|
66
|
-
end
|
32
|
+
output_ipa
|
67
33
|
|
68
|
-
|
34
|
+
publish_build_ipa if options.publish?
|
35
|
+
upload_build_mapping_file if options.mapping?
|
69
36
|
|
70
|
-
|
71
|
-
ipa_path = Dir["#{@output_path}/*.ipa"].first
|
72
|
-
publish(ipa_path, short: options[:short], changelog: options[:changelog], token: options[:token])
|
73
|
-
end
|
37
|
+
logger_info_blank_line
|
74
38
|
end
|
75
39
|
|
76
40
|
def build_apk *args, options
|
@@ -78,12 +42,89 @@ module FIR
|
|
78
42
|
|
79
43
|
private
|
80
44
|
|
81
|
-
def
|
45
|
+
def initialize_ipa_build_cmd args, options
|
46
|
+
ipa_build_cmd = "xcodebuild build -sdk iphoneos"
|
47
|
+
|
48
|
+
@configuration = options[:configuration]
|
49
|
+
@wrapper_name = File.basename(options[:name].to_s, '.*') + '.app' unless options[:name].blank?
|
50
|
+
@target_name = options[:target]
|
51
|
+
@scheme_name = options[:scheme]
|
52
|
+
@dsym_name = @wrapper_name + '.dSYM' unless @wrapper_name.blank?
|
53
|
+
|
54
|
+
if options.workspace?
|
55
|
+
workspace = check_and_find_ios_workspace(@build_dir)
|
56
|
+
check_ios_scheme(@scheme_name)
|
57
|
+
ipa_build_cmd += " -workspace '#{workspace}' -scheme '#{@scheme_name}'"
|
58
|
+
else
|
59
|
+
project = check_and_find_ios_project(@build_dir)
|
60
|
+
ipa_build_cmd += " -project '#{project}'"
|
61
|
+
end
|
62
|
+
|
63
|
+
ipa_build_cmd += " -configuration '#{@configuration}'" unless @configuration.blank?
|
64
|
+
ipa_build_cmd += " -target '#{@target_name}'" unless @target_name.blank?
|
65
|
+
ipa_build_cmd += " #{ipa_custom_settings(args)} 2>&1"
|
66
|
+
|
67
|
+
ipa_build_cmd
|
68
|
+
end
|
69
|
+
|
70
|
+
def ipa_custom_settings args
|
71
|
+
custom_settings = parse_ipa_custom_settings(args)
|
72
|
+
|
73
|
+
# convert { "a" => "1", "b" => "2" } => "a='1' b='2'"
|
74
|
+
setting_str = custom_settings.collect { |k, v| "#{k}='#{v}'" }.join(' ')
|
75
|
+
setting_str += " WRAPPER_NAME='#{@wrapper_name}'" unless @wrapper_name.blank?
|
76
|
+
setting_str += " TARGET_BUILD_DIR='#{@build_tmp_dir}'" unless custom_settings['TARGET_BUILD_DIR']
|
77
|
+
setting_str += " CONFIGURATION_BUILD_DIR='#{@build_tmp_dir}'" unless custom_settings['CONFIGURATION_BUILD_DIR']
|
78
|
+
setting_str += " DWARF_DSYM_FOLDER_PATH='#{@output_path}'" unless custom_settings['DWARF_DSYM_FOLDER_PATH']
|
79
|
+
setting_str += " DWARF_DSYM_FILE_NAME='#{@dsym_name}'" unless @dsym_name.blank?
|
80
|
+
setting_str
|
81
|
+
end
|
82
|
+
|
83
|
+
def output_ipa
|
84
|
+
FileUtils.mkdir_p(@output_path) unless File.exist?(@output_path)
|
85
|
+
Dir.chdir(@build_tmp_dir) do
|
86
|
+
apps = Dir["*.app"]
|
87
|
+
if apps.length == 0
|
88
|
+
logger.error "Builded has no output app, Can not be packaged"
|
89
|
+
exit 1
|
90
|
+
end
|
91
|
+
|
92
|
+
apps.each do |app|
|
93
|
+
ipa_path = File.join(@output_path, "#{File.basename(app, '.app')}.ipa")
|
94
|
+
zip_app2ipa(File.join(@build_tmp_dir, app), ipa_path)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
logger.info "Build Success"
|
99
|
+
end
|
100
|
+
|
101
|
+
def publish_build_ipa
|
102
|
+
logger_info_blank_line
|
103
|
+
publish Dir["#{@output_path}/*.ipa"].first, short: @short,
|
104
|
+
changelog: @changelog,
|
105
|
+
token: @token
|
106
|
+
end
|
107
|
+
|
108
|
+
def upload_build_mapping_file
|
109
|
+
logger_info_blank_line
|
110
|
+
|
111
|
+
@app_info = ipa_info(Dir["#{@output_path}/*.ipa"].first)
|
112
|
+
@mapping_file = Dir["#{@output_path}/*.dSYM/Contents/Resources/DWARF/*"].first
|
113
|
+
|
114
|
+
mapping @mapping_file, proj: @proj,
|
115
|
+
build: @app_info[:build],
|
116
|
+
version: @app_info[:version],
|
117
|
+
token: @token
|
118
|
+
end
|
119
|
+
|
120
|
+
# convert ['a=1', 'b=2'] => { 'a' => '1', 'b' => '2' }
|
121
|
+
def parse_ipa_custom_settings args
|
82
122
|
hash = {}
|
83
123
|
args.each do |setting|
|
84
124
|
k, v = setting.split('=', 2).map(&:strip)
|
85
125
|
hash[k] = v
|
86
126
|
end
|
127
|
+
|
87
128
|
hash
|
88
129
|
end
|
89
130
|
|
@@ -94,13 +135,13 @@ module FIR
|
|
94
135
|
end
|
95
136
|
end
|
96
137
|
|
97
|
-
def
|
138
|
+
def check_and_find_ios_project path
|
98
139
|
unless File.exist?(path)
|
99
140
|
logger.error "The first param BUILD_DIR must be a xcodeproj directory"
|
100
141
|
exit 1
|
101
142
|
end
|
102
143
|
|
103
|
-
if
|
144
|
+
if is_ios_project?(path)
|
104
145
|
project = path
|
105
146
|
else
|
106
147
|
project = Dir["#{path}/*.xcodeproj"].first
|
@@ -113,13 +154,13 @@ module FIR
|
|
113
154
|
project
|
114
155
|
end
|
115
156
|
|
116
|
-
def
|
157
|
+
def check_and_find_ios_workspace path
|
117
158
|
unless File.exist?(path)
|
118
159
|
logger.error "The first param BUILD_DIR must be a xcworkspace directory"
|
119
160
|
exit 1
|
120
161
|
end
|
121
162
|
|
122
|
-
if
|
163
|
+
if is_ios_workspace?(path)
|
123
164
|
workspace = path
|
124
165
|
else
|
125
166
|
workspace = Dir["#{path}/*.xcworkspace"].first
|
@@ -132,18 +173,18 @@ module FIR
|
|
132
173
|
workspace
|
133
174
|
end
|
134
175
|
|
135
|
-
def
|
176
|
+
def check_ios_scheme scheme_name
|
136
177
|
if scheme_name.blank?
|
137
178
|
logger.error "Must provide a scheme by `-S` option when build a workspace"
|
138
179
|
exit 1
|
139
180
|
end
|
140
181
|
end
|
141
182
|
|
142
|
-
def
|
183
|
+
def is_ios_project? path
|
143
184
|
File.extname(path) == '.xcodeproj'
|
144
185
|
end
|
145
186
|
|
146
|
-
def
|
187
|
+
def is_ios_workspace? path
|
147
188
|
File.extname(path) == '.xcworkspace'
|
148
189
|
end
|
149
190
|
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module FIR
|
4
|
+
module Config
|
5
|
+
CONFIG_PATH = "#{ENV['HOME']}/.fir-cli"
|
6
|
+
API_YML_PATH = File.expand_path("../../", __FILE__) + '/api.yml'
|
7
|
+
APP_FILE_TYPE = %w(.ipa .apk).freeze
|
8
|
+
|
9
|
+
def fir_api
|
10
|
+
@fir_api ||= YAML.load_file(API_YML_PATH).deep_symbolize_keys[:fir]
|
11
|
+
end
|
12
|
+
|
13
|
+
def bughd_api
|
14
|
+
@bughd_api ||= YAML.load_file(API_YML_PATH).deep_symbolize_keys[:bughd]
|
15
|
+
end
|
16
|
+
|
17
|
+
def config
|
18
|
+
@config ||= YAML.load_file(CONFIG_PATH).deep_symbolize_keys if File.exist?(CONFIG_PATH)
|
19
|
+
end
|
20
|
+
|
21
|
+
def reload_config
|
22
|
+
@config = YAML.load_file(CONFIG_PATH).deep_symbolize_keys
|
23
|
+
end
|
24
|
+
|
25
|
+
def write_config hash
|
26
|
+
File.open(CONFIG_PATH, 'w+') { |f| f << YAML.dump(hash) }
|
27
|
+
end
|
28
|
+
|
29
|
+
def current_token
|
30
|
+
@token ||= config[:token] if config
|
31
|
+
end
|
32
|
+
|
33
|
+
alias_method :☠, :exit
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module FIR
|
4
|
+
module Http
|
5
|
+
|
6
|
+
DEFAULT_TIMEOUT = 300
|
7
|
+
|
8
|
+
def get url, params = {}
|
9
|
+
begin
|
10
|
+
res = ::RestClient::Request.execute(
|
11
|
+
method: :get,
|
12
|
+
url: url,
|
13
|
+
timeout: DEFAULT_TIMEOUT,
|
14
|
+
headers: default_headers.merge(params: params)
|
15
|
+
)
|
16
|
+
rescue => e
|
17
|
+
logger.error e.message.to_s + " - " + e.response.to_s
|
18
|
+
exit 1
|
19
|
+
end
|
20
|
+
|
21
|
+
JSON.parse(res.body.force_encoding("UTF-8"), symbolize_names: true)
|
22
|
+
end
|
23
|
+
|
24
|
+
%w(post patch put).each do |method|
|
25
|
+
define_method method do |url, query|
|
26
|
+
begin
|
27
|
+
res = ::RestClient::Request.execute(
|
28
|
+
method: method.to_sym,
|
29
|
+
url: url,
|
30
|
+
payload: query,
|
31
|
+
timeout: DEFAULT_TIMEOUT,
|
32
|
+
headers: default_headers
|
33
|
+
)
|
34
|
+
rescue => e
|
35
|
+
logger.error e.message.to_s + " - " + e.response.to_s
|
36
|
+
exit 1
|
37
|
+
end
|
38
|
+
|
39
|
+
JSON.parse(res.body.force_encoding("UTF-8"), symbolize_names: true)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def default_headers
|
46
|
+
{ content_type: :json, source: 'fir-cli', version: FIR::VERSION }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/fir/util/info.rb
CHANGED
@@ -7,6 +7,7 @@ module FIR
|
|
7
7
|
file_path = File.absolute_path(args.first.to_s)
|
8
8
|
is_all = !options[:all].blank?
|
9
9
|
|
10
|
+
check_file_exist file_path
|
10
11
|
check_supported_file file_path
|
11
12
|
|
12
13
|
file_type = File.extname(file_path).delete('.')
|
@@ -16,10 +17,12 @@ module FIR
|
|
16
17
|
|
17
18
|
app_info = send("#{file_type}_info", file_path, is_all)
|
18
19
|
app_info.each { |k, v| logger.info "#{k}: #{v}" }
|
20
|
+
|
21
|
+
logger_info_blank_line
|
19
22
|
end
|
20
23
|
|
21
|
-
def ipa_info ipa_path, is_all
|
22
|
-
ipa = Parser::
|
24
|
+
def ipa_info ipa_path, is_all = false
|
25
|
+
ipa = FIR::Parser::Ipa.new(ipa_path)
|
23
26
|
app = ipa.app
|
24
27
|
|
25
28
|
info = {
|
@@ -52,7 +55,7 @@ module FIR
|
|
52
55
|
info
|
53
56
|
end
|
54
57
|
|
55
|
-
def apk_info apk_path, is_all
|
58
|
+
def apk_info apk_path, is_all = false
|
56
59
|
apk = Android::Apk.new(apk_path)
|
57
60
|
info = {
|
58
61
|
type: 'android',
|
data/lib/fir/util/login.rb
CHANGED
@@ -0,0 +1,65 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module FIR
|
4
|
+
module Mapping
|
5
|
+
|
6
|
+
def mapping *args, options
|
7
|
+
@file_path = File.absolute_path(args.first.to_s)
|
8
|
+
@token = options[:token] || current_token
|
9
|
+
@proj = options[:proj].to_s
|
10
|
+
@version = options[:version].to_s
|
11
|
+
@build = options[:build].to_s
|
12
|
+
|
13
|
+
check_file_exist @file_path
|
14
|
+
check_token_cannot_be_blank @token
|
15
|
+
check_project_id_cannot_be_blank
|
16
|
+
|
17
|
+
logger.info "Creating bughd project's version......."
|
18
|
+
logger_info_dividing_line
|
19
|
+
|
20
|
+
@full_version = find_or_create_bughd_full_version
|
21
|
+
|
22
|
+
logger.info "Uploading mapping file......."
|
23
|
+
logger_info_dividing_line
|
24
|
+
|
25
|
+
upload_mapping_file
|
26
|
+
|
27
|
+
logger.info "Uploaded succeed: #{bughd_api[:domain]}/project/#{@proj}/settings"
|
28
|
+
logger_info_blank_line
|
29
|
+
end
|
30
|
+
|
31
|
+
def find_or_create_bughd_full_version
|
32
|
+
url = bughd_api[:project_url] + "/#{@proj}/full_versions"
|
33
|
+
post url, version: @version, build: @build, uuid: uuid
|
34
|
+
end
|
35
|
+
|
36
|
+
def upload_mapping_file
|
37
|
+
if File.is_dsym?(@file_path)
|
38
|
+
tmp_file_path = "#{Dir.tmpdir}/#{File.basename(@file_path)}-fircli.dSYM"
|
39
|
+
FileUtils.cp(@file_path, tmp_file_path)
|
40
|
+
elsif File.is_txt?(@file_path)
|
41
|
+
tmp_file_path = "#{Dir.tmpdir}/#{File.basename(@file_path)}-fircli.txt"
|
42
|
+
FileUtils.cp(@file_path, tmp_file_path)
|
43
|
+
else
|
44
|
+
tmp_file_path = @file_path
|
45
|
+
end
|
46
|
+
|
47
|
+
url = bughd_api[:full_version_url] + "/#{@full_version[:id]}"
|
48
|
+
patch url, file: File.new(tmp_file_path, 'rb'), project_id: @proj, uuid: uuid
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def check_project_id_cannot_be_blank
|
54
|
+
if @proj.blank?
|
55
|
+
logger.error "Project id can't be blank"
|
56
|
+
exit 1
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def uuid
|
61
|
+
@uuid ||= fetch_user_uuid(@token)
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
data/lib/fir/util/me.rb
CHANGED
@@ -6,13 +6,14 @@ module FIR
|
|
6
6
|
def me
|
7
7
|
check_logined
|
8
8
|
|
9
|
-
|
10
|
-
email = user_info.fetch(:email, '')
|
11
|
-
name = user_info.fetch(:name, '')
|
9
|
+
user_info = fetch_user_info(current_token)
|
12
10
|
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
16
17
|
end
|
17
18
|
end
|
18
19
|
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module FIR
|
4
|
+
module Parser
|
5
|
+
|
6
|
+
class Ipa
|
7
|
+
|
8
|
+
def initialize(path)
|
9
|
+
@path = path
|
10
|
+
end
|
11
|
+
|
12
|
+
def app
|
13
|
+
@app ||= App.new(app_path)
|
14
|
+
end
|
15
|
+
|
16
|
+
def app_path
|
17
|
+
@app_path ||= Dir.glob(File.join(contents, 'Payload', '*.app')).first
|
18
|
+
end
|
19
|
+
|
20
|
+
def cleanup
|
21
|
+
return unless @contents
|
22
|
+
FileUtils.rm_rf(@contents)
|
23
|
+
@contents = nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def metadata
|
27
|
+
return unless has_metadata?
|
28
|
+
@metadata ||= CFPropertyList.native_types(CFPropertyList::List.new(file: metadata_path).value)
|
29
|
+
end
|
30
|
+
|
31
|
+
def has_metadata?
|
32
|
+
File.file? metadata_path
|
33
|
+
end
|
34
|
+
|
35
|
+
def metadata_path
|
36
|
+
@metadata_path ||= File.join(@contents, 'iTunesMetadata.plist')
|
37
|
+
end
|
38
|
+
|
39
|
+
def release_type
|
40
|
+
has_metadata? ? 'store' : 'adhoc'
|
41
|
+
end
|
42
|
+
|
43
|
+
def contents
|
44
|
+
return if @contents
|
45
|
+
@contents = "fir-cli_tmp/ipa_files-#{Time.now.to_i}"
|
46
|
+
|
47
|
+
Zip::File.open(@path) do |zip_file|
|
48
|
+
zip_file.each do |f|
|
49
|
+
f_path = File.join(@contents, f.name)
|
50
|
+
FileUtils.mkdir_p(File.dirname(f_path))
|
51
|
+
zip_file.extract(f, f_path) unless File.exist?(f_path)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
@contents
|
56
|
+
end
|
57
|
+
|
58
|
+
class App
|
59
|
+
|
60
|
+
def initialize(path)
|
61
|
+
@path = path
|
62
|
+
end
|
63
|
+
|
64
|
+
def info
|
65
|
+
@info ||= CFPropertyList.native_types(
|
66
|
+
CFPropertyList::List.new(file: File.join(@path, 'Info.plist')).value)
|
67
|
+
end
|
68
|
+
|
69
|
+
def name
|
70
|
+
info['CFBundleName']
|
71
|
+
end
|
72
|
+
|
73
|
+
def identifier
|
74
|
+
info['CFBundleIdentifier']
|
75
|
+
end
|
76
|
+
|
77
|
+
def display_name
|
78
|
+
info['CFBundleDisplayName']
|
79
|
+
end
|
80
|
+
|
81
|
+
def version
|
82
|
+
info['CFBundleVersion']
|
83
|
+
end
|
84
|
+
|
85
|
+
def short_version
|
86
|
+
info['CFBundleShortVersionString']
|
87
|
+
end
|
88
|
+
|
89
|
+
def icons
|
90
|
+
@icons ||= begin
|
91
|
+
icons = []
|
92
|
+
info['CFBundleIcons']['CFBundlePrimaryIcon']['CFBundleIconFiles'].each do |name|
|
93
|
+
icons << get_image(name)
|
94
|
+
icons << get_image("#{name}@2x")
|
95
|
+
end
|
96
|
+
icons.delete_if { |i| !i }
|
97
|
+
rescue NoMethodError
|
98
|
+
[]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def mobileprovision
|
103
|
+
return unless has_mobileprovision?
|
104
|
+
return @mobileprovision if @mobileprovision
|
105
|
+
|
106
|
+
cmd = "security cms -D -i \"#{mobileprovision_path}\""
|
107
|
+
begin
|
108
|
+
@mobileprovision = CFPropertyList.native_types(CFPropertyList::List.new(data: `#{cmd}`).value)
|
109
|
+
rescue CFFormatError
|
110
|
+
@mobileprovision = {}
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def has_mobileprovision?
|
115
|
+
File.file? mobileprovision_path
|
116
|
+
end
|
117
|
+
|
118
|
+
def mobileprovision_path
|
119
|
+
@mobileprovision_path ||= File.join(@path, 'embedded.mobileprovision')
|
120
|
+
end
|
121
|
+
|
122
|
+
def hide_developer_certificates
|
123
|
+
mobileprovision.delete('DeveloperCertificates') if has_mobileprovision?
|
124
|
+
end
|
125
|
+
|
126
|
+
def devices
|
127
|
+
mobileprovision['ProvisionedDevices'] if has_mobileprovision?
|
128
|
+
end
|
129
|
+
|
130
|
+
def distribution_name
|
131
|
+
"#{mobileprovision['Name']} - #{mobileprovision['TeamName']}" if has_mobileprovision?
|
132
|
+
end
|
133
|
+
|
134
|
+
def release_type
|
135
|
+
if has_mobileprovision?
|
136
|
+
if devices
|
137
|
+
'adhoc'
|
138
|
+
else
|
139
|
+
'inhouse'
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
private
|
145
|
+
|
146
|
+
def get_image name
|
147
|
+
path = File.join(@path, "#{name}.png")
|
148
|
+
return nil unless File.exist?(path)
|
149
|
+
path
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
class Apk
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|