app-info 2.0.0.beta3 → 2.0.0.beta4
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/CHANGELOG.md +8 -7
- data/README.md +7 -7
- data/lib/app_info.rb +19 -9
- data/lib/app_info/apk.rb +184 -0
- data/lib/app_info/core_ext/object/try.rb +2 -0
- data/lib/app_info/dsym.rb +149 -0
- data/lib/app_info/ipa.rb +230 -0
- data/lib/app_info/ipa/info_plist.rb +133 -0
- data/lib/app_info/ipa/mobile_provision.rb +89 -0
- data/lib/app_info/util.rb +26 -0
- data/lib/app_info/version.rb +1 -1
- metadata +7 -7
- data/lib/app_info/parser.rb +0 -40
- data/lib/app_info/parser/apk.rb +0 -183
- data/lib/app_info/parser/dsym.rb +0 -126
- data/lib/app_info/parser/ipa.rb +0 -229
- data/lib/app_info/parser/ipa/info_plist.rb +0 -135
- data/lib/app_info/parser/ipa/mobile_provision.rb +0 -91
data/lib/app_info/ipa.rb
ADDED
@@ -0,0 +1,230 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'zip'
|
4
|
+
require 'pngdefry'
|
5
|
+
require 'fileutils'
|
6
|
+
require 'securerandom'
|
7
|
+
require 'cfpropertylist'
|
8
|
+
require 'app_info/util'
|
9
|
+
require 'app_info/core_ext/object/try'
|
10
|
+
|
11
|
+
module AppInfo
|
12
|
+
# IPA parser
|
13
|
+
class IPA
|
14
|
+
attr_reader :file
|
15
|
+
|
16
|
+
# iOS Export types
|
17
|
+
module ExportType
|
18
|
+
DEBUG = 'Debug'
|
19
|
+
ADHOC = 'AdHoc'
|
20
|
+
INHOUSE = 'inHouse'
|
21
|
+
RELEASE = 'Release'
|
22
|
+
UNKOWN = nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(file)
|
26
|
+
@file = file
|
27
|
+
end
|
28
|
+
|
29
|
+
def size(humanable = false)
|
30
|
+
AppInfo::Util.file_size(@file, humanable)
|
31
|
+
end
|
32
|
+
|
33
|
+
def os
|
34
|
+
AppInfo::Platform::IOS
|
35
|
+
end
|
36
|
+
alias file_type os
|
37
|
+
|
38
|
+
def iphone?
|
39
|
+
info.iphone?
|
40
|
+
end
|
41
|
+
|
42
|
+
def ipad?
|
43
|
+
info.ipad?
|
44
|
+
end
|
45
|
+
|
46
|
+
def universal?
|
47
|
+
info.universal?
|
48
|
+
end
|
49
|
+
|
50
|
+
def build_version
|
51
|
+
info.build_version
|
52
|
+
end
|
53
|
+
|
54
|
+
def release_version
|
55
|
+
info.release_version
|
56
|
+
end
|
57
|
+
|
58
|
+
def identifier
|
59
|
+
info.identifier
|
60
|
+
end
|
61
|
+
|
62
|
+
def name
|
63
|
+
display_name || bundle_name
|
64
|
+
end
|
65
|
+
|
66
|
+
def display_name
|
67
|
+
info.display_name
|
68
|
+
end
|
69
|
+
|
70
|
+
def bundle_name
|
71
|
+
info.bundle_name
|
72
|
+
end
|
73
|
+
|
74
|
+
def icons
|
75
|
+
info.icons
|
76
|
+
end
|
77
|
+
|
78
|
+
#
|
79
|
+
# Return the minimum OS version for the given application
|
80
|
+
#
|
81
|
+
def min_sdk_version
|
82
|
+
info.min_sdk_version
|
83
|
+
end
|
84
|
+
|
85
|
+
def device_type
|
86
|
+
info.device_type
|
87
|
+
end
|
88
|
+
|
89
|
+
def devices
|
90
|
+
mobileprovision.devices
|
91
|
+
end
|
92
|
+
|
93
|
+
def team_name
|
94
|
+
mobileprovision.team_name
|
95
|
+
end
|
96
|
+
|
97
|
+
def team_identifier
|
98
|
+
mobileprovision.team_identifier
|
99
|
+
end
|
100
|
+
|
101
|
+
def profile_name
|
102
|
+
mobileprovision.profile_name
|
103
|
+
end
|
104
|
+
|
105
|
+
def expired_date
|
106
|
+
mobileprovision.expired_date
|
107
|
+
end
|
108
|
+
|
109
|
+
def distribution_name
|
110
|
+
"#{profile_name} - #{team_name}" if profile_name && team_name
|
111
|
+
end
|
112
|
+
|
113
|
+
def release_type
|
114
|
+
if stored?
|
115
|
+
ExportType::RELEASE
|
116
|
+
else
|
117
|
+
build_type
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def build_type
|
122
|
+
if mobileprovision?
|
123
|
+
if devices
|
124
|
+
ExportType::ADHOC
|
125
|
+
else
|
126
|
+
ExportType::INHOUSE
|
127
|
+
end
|
128
|
+
else
|
129
|
+
ExportType::DEBUG
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def stored?
|
134
|
+
metadata? ? true : false
|
135
|
+
end
|
136
|
+
|
137
|
+
def hide_developer_certificates
|
138
|
+
mobileprovision.delete('DeveloperCertificates') if mobileprovision?
|
139
|
+
end
|
140
|
+
|
141
|
+
def mobileprovision
|
142
|
+
return unless mobileprovision?
|
143
|
+
return @mobileprovision if @mobileprovision
|
144
|
+
|
145
|
+
@mobileprovision = MobileProvision.new(mobileprovision_path)
|
146
|
+
end
|
147
|
+
|
148
|
+
def mobileprovision?
|
149
|
+
File.exist?mobileprovision_path
|
150
|
+
end
|
151
|
+
|
152
|
+
def mobileprovision_path
|
153
|
+
filename = 'embedded.mobileprovision'
|
154
|
+
@mobileprovision_path ||= File.join(@file, filename)
|
155
|
+
unless File.exist?(@mobileprovision_path)
|
156
|
+
@mobileprovision_path = File.join(app_path, filename)
|
157
|
+
end
|
158
|
+
|
159
|
+
@mobileprovision_path
|
160
|
+
end
|
161
|
+
|
162
|
+
def metadata
|
163
|
+
return unless metadata?
|
164
|
+
|
165
|
+
@metadata ||= CFPropertyList.native_types(CFPropertyList::List.new(file: metadata_path).value)
|
166
|
+
end
|
167
|
+
|
168
|
+
def metadata?
|
169
|
+
File.exist?(metadata_path)
|
170
|
+
end
|
171
|
+
|
172
|
+
def metadata_path
|
173
|
+
@metadata_path ||= File.join(contents, 'iTunesMetadata.plist')
|
174
|
+
end
|
175
|
+
|
176
|
+
def info
|
177
|
+
@info ||= InfoPlist.new(app_path)
|
178
|
+
end
|
179
|
+
|
180
|
+
def app_path
|
181
|
+
@app_path ||= Dir.glob(File.join(contents, 'Payload', '*.app')).first
|
182
|
+
end
|
183
|
+
|
184
|
+
def cleanup!
|
185
|
+
return unless @contents
|
186
|
+
|
187
|
+
FileUtils.rm_rf(@contents)
|
188
|
+
|
189
|
+
@contents = nil
|
190
|
+
@icons = nil
|
191
|
+
@app_path = nil
|
192
|
+
@metadata = nil
|
193
|
+
@metadata_path = nil
|
194
|
+
@info = nil
|
195
|
+
end
|
196
|
+
|
197
|
+
alias bundle_id identifier
|
198
|
+
|
199
|
+
private
|
200
|
+
|
201
|
+
def contents
|
202
|
+
# source: https://github.com/soffes/lagunitas/blob/master/lib/lagunitas/ipa.rb
|
203
|
+
unless @contents
|
204
|
+
@contents = "#{Dir.mktmpdir}/AppInfo-ios-#{SecureRandom.hex}"
|
205
|
+
Zip::File.open(@file) do |zip_file|
|
206
|
+
zip_file.each do |f|
|
207
|
+
f_path = File.join(@contents, f.name)
|
208
|
+
zip_file.extract(f, f_path) unless File.exist?(f_path)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
@contents
|
214
|
+
end
|
215
|
+
|
216
|
+
def icons_root_path
|
217
|
+
iphone = 'CFBundleIcons'
|
218
|
+
ipad = 'CFBundleIcons~ipad'
|
219
|
+
|
220
|
+
case device_type
|
221
|
+
when 'iPhone'
|
222
|
+
[iphone]
|
223
|
+
when 'iPad'
|
224
|
+
[ipad]
|
225
|
+
when 'Universal'
|
226
|
+
[iphone, ipad]
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cfpropertylist'
|
4
|
+
|
5
|
+
module AppInfo
|
6
|
+
# iOS Info.plist parser
|
7
|
+
class InfoPlist
|
8
|
+
def initialize(app_path)
|
9
|
+
@app_path = app_path
|
10
|
+
end
|
11
|
+
|
12
|
+
def build_version
|
13
|
+
info.try(:[], 'CFBundleVersion')
|
14
|
+
end
|
15
|
+
|
16
|
+
def release_version
|
17
|
+
info.try(:[], 'CFBundleShortVersionString')
|
18
|
+
end
|
19
|
+
|
20
|
+
def identifier
|
21
|
+
info.try(:[], 'CFBundleIdentifier')
|
22
|
+
end
|
23
|
+
alias bundle_id identifier
|
24
|
+
|
25
|
+
def name
|
26
|
+
display_name || bundle_name
|
27
|
+
end
|
28
|
+
|
29
|
+
def display_name
|
30
|
+
info.try(:[], 'CFBundleDisplayName')
|
31
|
+
end
|
32
|
+
|
33
|
+
def bundle_name
|
34
|
+
info.try(:[], 'CFBundleName')
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# Extract the Minimum OS Version from the Info.plist
|
39
|
+
#
|
40
|
+
def min_sdk_version
|
41
|
+
info.try(:[], 'MinimumOSVersion')
|
42
|
+
end
|
43
|
+
|
44
|
+
def icons
|
45
|
+
return @icons if @icons
|
46
|
+
|
47
|
+
@icons = []
|
48
|
+
icons_root_path.each do |name|
|
49
|
+
icon_array = info.try(:[], name)
|
50
|
+
.try(:[], 'CFBundlePrimaryIcon')
|
51
|
+
.try(:[], 'CFBundleIconFiles')
|
52
|
+
|
53
|
+
next if icon_array.nil? || icon_array.zero?
|
54
|
+
|
55
|
+
icon_array.each do |items|
|
56
|
+
Dir.glob(File.join(@app_path, "#{items}*")).find_all.each do |file|
|
57
|
+
dict = {
|
58
|
+
name: File.basename(file),
|
59
|
+
file: file,
|
60
|
+
dimensions: Pngdefry.dimensions(file)
|
61
|
+
}
|
62
|
+
|
63
|
+
@icons.push(dict)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
@icons
|
69
|
+
end
|
70
|
+
|
71
|
+
def device_type
|
72
|
+
device_family = info.try(:[], 'UIDeviceFamily')
|
73
|
+
if device_family.length == 1
|
74
|
+
case device_family
|
75
|
+
when [1]
|
76
|
+
'iPhone'
|
77
|
+
when [2]
|
78
|
+
'iPad'
|
79
|
+
end
|
80
|
+
elsif device_family.length == 2 && device_family == [1, 2]
|
81
|
+
'Universal'
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def iphone?
|
86
|
+
device_type == 'iPhone'
|
87
|
+
end
|
88
|
+
|
89
|
+
def ipad?
|
90
|
+
device_type == 'iPad'
|
91
|
+
end
|
92
|
+
|
93
|
+
def universal?
|
94
|
+
device_type == 'Universal'
|
95
|
+
end
|
96
|
+
|
97
|
+
def release_type
|
98
|
+
if stored?
|
99
|
+
'Store'
|
100
|
+
else
|
101
|
+
build_type
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def [](key)
|
106
|
+
info.try(:[], key.to_s)
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
def info
|
112
|
+
@info ||= CFPropertyList.native_types(CFPropertyList::List.new(file: info_path).value)
|
113
|
+
end
|
114
|
+
|
115
|
+
def info_path
|
116
|
+
File.join(@app_path, 'Info.plist')
|
117
|
+
end
|
118
|
+
|
119
|
+
IPHONE_KEY = 'CFBundleIcons'
|
120
|
+
IPAD_KEY = 'CFBundleIcons~ipad'
|
121
|
+
|
122
|
+
def icons_root_path
|
123
|
+
case device_type
|
124
|
+
when 'iPhone'
|
125
|
+
[IPHONE_KEY]
|
126
|
+
when 'iPad'
|
127
|
+
[IPAD_KEY]
|
128
|
+
when 'Universal'
|
129
|
+
[IPHONE_KEY, IPAD_KEY]
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cfpropertylist'
|
4
|
+
|
5
|
+
module AppInfo
|
6
|
+
# .mobileprovision file parser
|
7
|
+
class MobileProvision
|
8
|
+
def initialize(path)
|
9
|
+
@path = path
|
10
|
+
end
|
11
|
+
|
12
|
+
def name
|
13
|
+
mobileprovision.try(:[], 'Name')
|
14
|
+
end
|
15
|
+
|
16
|
+
def app_name
|
17
|
+
mobileprovision.try(:[], 'AppIDName')
|
18
|
+
end
|
19
|
+
|
20
|
+
def devices
|
21
|
+
mobileprovision.try(:[], 'ProvisionedDevices')
|
22
|
+
end
|
23
|
+
|
24
|
+
def team_identifier
|
25
|
+
mobileprovision.try(:[], 'TeamIdentifier')
|
26
|
+
end
|
27
|
+
|
28
|
+
def team_name
|
29
|
+
mobileprovision.try(:[], 'TeamName')
|
30
|
+
end
|
31
|
+
|
32
|
+
def profile_name
|
33
|
+
mobileprovision.try(:[], 'Name')
|
34
|
+
end
|
35
|
+
|
36
|
+
def created_date
|
37
|
+
mobileprovision.try(:[], 'CreationDate')
|
38
|
+
end
|
39
|
+
|
40
|
+
def expired_date
|
41
|
+
mobileprovision.try(:[], 'ExpirationDate')
|
42
|
+
end
|
43
|
+
|
44
|
+
def entitlements
|
45
|
+
mobileprovision.try(:[], 'Entitlements')
|
46
|
+
end
|
47
|
+
|
48
|
+
def method_missing(method_name, *args, &block)
|
49
|
+
key = if method_name.to_s.include?('_')
|
50
|
+
method_name.to_s
|
51
|
+
.split('_')
|
52
|
+
.map(&:capitalize)
|
53
|
+
.join('')
|
54
|
+
else
|
55
|
+
method_name.to_s
|
56
|
+
end
|
57
|
+
|
58
|
+
mobileprovision.try(:[], key)
|
59
|
+
end
|
60
|
+
|
61
|
+
def empty?
|
62
|
+
mobileprovision.nil?
|
63
|
+
end
|
64
|
+
|
65
|
+
def mobileprovision
|
66
|
+
return @mobileprovision = nil unless File.exist?(@path)
|
67
|
+
|
68
|
+
data = File.read(@path)
|
69
|
+
data = strip_plist_wrapper(data) unless bplist?(data)
|
70
|
+
list = CFPropertyList::List.new(data: data).value
|
71
|
+
@mobileprovision = CFPropertyList.native_types(list)
|
72
|
+
rescue CFFormatError
|
73
|
+
@mobileprovision = nil
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def bplist?(raw)
|
79
|
+
raw[0..5] == 'bplist'
|
80
|
+
end
|
81
|
+
|
82
|
+
def strip_plist_wrapper(raw)
|
83
|
+
end_tag = '</plist>'
|
84
|
+
start_point = raw.index('<?xml version=')
|
85
|
+
end_point = raw.index(end_tag) + end_tag.size - 1
|
86
|
+
raw[start_point..end_point]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|