fir-cli 1.1.5 → 1.1.7
Sign up to get free protection for your applications and to get access to all the features.
- 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
|