app-info 2.8.5 → 3.0.0.beta2
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/.github/dependabot.yml +12 -7
- data/.github/workflows/ci.yml +3 -1
- data/.rubocop.yml +33 -11
- data/CHANGELOG.md +34 -1
- data/Gemfile +7 -1
- data/README.md +68 -13
- data/Rakefile +11 -0
- data/app_info.gemspec +12 -3
- data/lib/app_info/aab.rb +48 -108
- data/lib/app_info/android/signature.rb +114 -0
- data/lib/app_info/android/signatures/base.rb +53 -0
- data/lib/app_info/android/signatures/info.rb +158 -0
- data/lib/app_info/android/signatures/v1.rb +63 -0
- data/lib/app_info/android/signatures/v2.rb +121 -0
- data/lib/app_info/android/signatures/v3.rb +131 -0
- data/lib/app_info/android/signatures/v4.rb +18 -0
- data/lib/app_info/android.rb +162 -0
- data/lib/app_info/apk.rb +54 -111
- data/lib/app_info/apple.rb +192 -0
- data/lib/app_info/certificate.rb +175 -0
- data/lib/app_info/const.rb +75 -0
- data/lib/app_info/core_ext/object/try.rb +3 -1
- data/lib/app_info/core_ext/string/inflector.rb +2 -0
- data/lib/app_info/dsym/debug_info.rb +72 -0
- data/lib/app_info/dsym/macho.rb +55 -0
- data/lib/app_info/dsym.rb +31 -135
- data/lib/app_info/error.rb +2 -2
- data/lib/app_info/file.rb +49 -0
- data/lib/app_info/helper/archive.rb +37 -0
- data/lib/app_info/helper/file_size.rb +25 -0
- data/lib/app_info/helper/generate_class.rb +29 -0
- data/lib/app_info/helper/protobuf.rb +12 -0
- data/lib/app_info/helper/signatures.rb +229 -0
- data/lib/app_info/helper.rb +5 -126
- data/lib/app_info/info_plist.rb +66 -29
- data/lib/app_info/ipa/framework.rb +4 -4
- data/lib/app_info/ipa.rb +61 -135
- data/lib/app_info/macos.rb +54 -102
- data/lib/app_info/mobile_provision.rb +67 -49
- data/lib/app_info/pe.rb +260 -0
- data/lib/app_info/png_uncrush.rb +24 -4
- data/lib/app_info/proguard.rb +29 -16
- data/lib/app_info/protobuf/manifest.rb +6 -3
- data/lib/app_info/protobuf/models/Configuration_pb.rb +1 -0
- data/lib/app_info/protobuf/models/README.md +7 -0
- data/lib/app_info/protobuf/models/Resources_pb.rb +2 -0
- data/lib/app_info/protobuf/resources.rb +5 -5
- data/lib/app_info/version.rb +1 -1
- data/lib/app_info.rb +90 -46
- metadata +48 -35
data/lib/app_info/ipa.rb
CHANGED
@@ -6,179 +6,106 @@ require 'forwardable'
|
|
6
6
|
require 'cfpropertylist'
|
7
7
|
|
8
8
|
module AppInfo
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
#
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
def initialize(file)
|
29
|
-
@file = file
|
30
|
-
end
|
31
|
-
|
32
|
-
def size(human_size: false)
|
33
|
-
file_to_human_size(@file, human_size: human_size)
|
34
|
-
end
|
35
|
-
|
36
|
-
def os
|
37
|
-
Platform::IOS
|
38
|
-
end
|
39
|
-
alias file_type os
|
40
|
-
|
41
|
-
def_delegators :info, :iphone?, :ipad?, :universal?, :build_version, :name,
|
42
|
-
:release_version, :identifier, :bundle_id, :display_name,
|
43
|
-
:bundle_name, :min_sdk_version, :min_os_version, :device_type
|
44
|
-
|
45
|
-
def_delegators :mobileprovision, :devices, :team_name, :team_identifier,
|
46
|
-
:profile_name, :expired_date
|
47
|
-
|
48
|
-
def distribution_name
|
49
|
-
"#{profile_name} - #{team_name}" if profile_name && team_name
|
50
|
-
end
|
51
|
-
|
52
|
-
def release_type
|
53
|
-
if stored?
|
54
|
-
ExportType::RELEASE
|
55
|
-
else
|
56
|
-
build_type
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def build_type
|
61
|
-
if mobileprovision?
|
62
|
-
if devices
|
63
|
-
ExportType::ADHOC
|
64
|
-
else
|
65
|
-
ExportType::ENTERPRISE
|
66
|
-
end
|
67
|
-
else
|
68
|
-
ExportType::DEBUG
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def archs
|
73
|
-
return unless File.exist?(bundle_path)
|
74
|
-
|
75
|
-
file = MachO.open(bundle_path)
|
76
|
-
case file
|
77
|
-
when MachO::MachOFile
|
78
|
-
[file.cpusubtype]
|
79
|
-
else
|
80
|
-
file.machos.each_with_object([]) do |arch, obj|
|
81
|
-
obj << arch.cpusubtype
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
alias architectures archs
|
86
|
-
|
9
|
+
class IPA < Apple
|
10
|
+
# Full icons metadata
|
11
|
+
# @example
|
12
|
+
# aab.icons
|
13
|
+
# # => [
|
14
|
+
# # {
|
15
|
+
# # name: 'icon.png',
|
16
|
+
# # file: '/path/to/icon.png',
|
17
|
+
# # uncrushed_file: '/path/to/uncrushed_icon.png',
|
18
|
+
# # dimensions: [64, 64]
|
19
|
+
# # },
|
20
|
+
# # {
|
21
|
+
# # name: 'icon1.png',
|
22
|
+
# # file: '/path/to/icon1.png',
|
23
|
+
# # uncrushed_file: '/path/to/uncrushed_icon1.png',
|
24
|
+
# # dimensions: [120, 120]
|
25
|
+
# # }
|
26
|
+
# # ]
|
27
|
+
# @return [Array<Hash{Symbol => String, Array<Integer>}>] icons paths of icons
|
87
28
|
def icons(uncrush: true)
|
88
29
|
@icons ||= icons_path.each_with_object([]) do |file, obj|
|
89
30
|
obj << build_icon_metadata(file, uncrush: uncrush)
|
90
31
|
end
|
91
32
|
end
|
92
33
|
|
34
|
+
# @return [Boolean]
|
93
35
|
def stored?
|
94
36
|
!!metadata?
|
95
37
|
end
|
96
38
|
|
39
|
+
# @return [Array<Plugin>]
|
97
40
|
def plugins
|
98
41
|
@plugins ||= Plugin.parse(app_path)
|
99
42
|
end
|
100
43
|
|
44
|
+
# @return [Array<Framework>]
|
101
45
|
def frameworks
|
102
46
|
@frameworks ||= Framework.parse(app_path)
|
103
47
|
end
|
104
48
|
|
105
|
-
|
106
|
-
mobileprovision.delete('DeveloperCertificates') if mobileprovision?
|
107
|
-
end
|
108
|
-
|
109
|
-
def mobileprovision
|
110
|
-
return unless mobileprovision?
|
111
|
-
return @mobileprovision if @mobileprovision
|
112
|
-
|
113
|
-
@mobileprovision = MobileProvision.new(mobileprovision_path)
|
114
|
-
end
|
115
|
-
|
116
|
-
def mobileprovision?
|
117
|
-
File.exist?(mobileprovision_path)
|
118
|
-
end
|
119
|
-
|
49
|
+
# @return [String]
|
120
50
|
def mobileprovision_path
|
121
51
|
filename = 'embedded.mobileprovision'
|
122
|
-
@mobileprovision_path ||= File.join(@file, filename)
|
123
|
-
unless File.exist?(@mobileprovision_path)
|
124
|
-
@mobileprovision_path = File.join(app_path, filename)
|
52
|
+
@mobileprovision_path ||= ::File.join(@file, filename)
|
53
|
+
unless ::File.exist?(@mobileprovision_path)
|
54
|
+
@mobileprovision_path = ::File.join(app_path, filename)
|
125
55
|
end
|
126
56
|
|
127
57
|
@mobileprovision_path
|
128
58
|
end
|
129
59
|
|
60
|
+
# @return [CFPropertyList]
|
130
61
|
def metadata
|
131
62
|
return unless metadata?
|
132
63
|
|
133
64
|
@metadata ||= CFPropertyList.native_types(CFPropertyList::List.new(file: metadata_path).value)
|
134
65
|
end
|
135
66
|
|
67
|
+
# @return [Boolean]
|
136
68
|
def metadata?
|
137
|
-
File.exist?(metadata_path)
|
69
|
+
::File.exist?(metadata_path)
|
138
70
|
end
|
139
71
|
|
72
|
+
# @return [String]
|
140
73
|
def metadata_path
|
141
|
-
@metadata_path ||= File.join(contents, 'iTunesMetadata.plist')
|
74
|
+
@metadata_path ||= ::File.join(contents, 'iTunesMetadata.plist')
|
142
75
|
end
|
143
76
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
def info
|
149
|
-
@info ||= InfoPlist.new(info_path)
|
77
|
+
# @return [String]
|
78
|
+
def binary_path
|
79
|
+
@binary_path ||= ::File.join(app_path, info.bundle_name)
|
150
80
|
end
|
151
81
|
|
82
|
+
# @return [String]
|
152
83
|
def info_path
|
153
|
-
@info_path ||= File.join(app_path, 'Info.plist')
|
84
|
+
@info_path ||= ::File.join(app_path, 'Info.plist')
|
154
85
|
end
|
155
86
|
|
87
|
+
# @return [String]
|
156
88
|
def app_path
|
157
|
-
@app_path ||= Dir.glob(File.join(contents, 'Payload', '*.app')).first
|
89
|
+
@app_path ||= Dir.glob(::File.join(contents, 'Payload', '*.app')).first
|
158
90
|
end
|
159
91
|
|
160
|
-
|
161
|
-
IPAD_KEY = 'CFBundleIcons~ipad'
|
162
|
-
|
92
|
+
# @return [Array<String>]
|
163
93
|
def icons_path
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
@icons_path << file
|
94
|
+
@icons_path ||= lambda {
|
95
|
+
icon_keys.each_with_object([]) do |name, icons|
|
96
|
+
filenames = info.try(:[], name)
|
97
|
+
.try(:[], 'CFBundlePrimaryIcon')
|
98
|
+
.try(:[], 'CFBundleIconFiles')
|
99
|
+
|
100
|
+
next if filenames.nil? || filenames.empty?
|
101
|
+
|
102
|
+
filenames.each do |filename|
|
103
|
+
Dir.glob(::File.join(app_path, "#{filename}*")).find_all.each do |file|
|
104
|
+
icons << file
|
105
|
+
end
|
177
106
|
end
|
178
107
|
end
|
179
|
-
|
180
|
-
|
181
|
-
@icons_path
|
108
|
+
}.call
|
182
109
|
end
|
183
110
|
|
184
111
|
def clear!
|
@@ -196,17 +123,13 @@ module AppInfo
|
|
196
123
|
@icons = nil
|
197
124
|
end
|
198
125
|
|
199
|
-
def contents
|
200
|
-
@contents ||= unarchive(@file, path: 'ios')
|
201
|
-
end
|
202
|
-
|
203
126
|
private
|
204
127
|
|
205
128
|
def build_icon_metadata(file, uncrush: true)
|
206
129
|
uncrushed_file = uncrush ? uncrush_png(file) : nil
|
207
130
|
|
208
131
|
{
|
209
|
-
name: File.basename(file),
|
132
|
+
name: ::File.basename(file),
|
210
133
|
file: file,
|
211
134
|
uncrushed_file: uncrushed_file,
|
212
135
|
dimensions: PngUncrush.dimensions(file)
|
@@ -217,16 +140,19 @@ module AppInfo
|
|
217
140
|
def uncrush_png(src_file)
|
218
141
|
dest_file = tempdir(src_file, prefix: 'uncrushed')
|
219
142
|
PngUncrush.decompress(src_file, dest_file)
|
220
|
-
File.exist?(dest_file) ? dest_file : nil
|
143
|
+
::File.exist?(dest_file) ? dest_file : nil
|
221
144
|
end
|
222
145
|
|
146
|
+
IPHONE_KEY = 'CFBundleIcons'
|
147
|
+
IPAD_KEY = 'CFBundleIcons~ipad'
|
148
|
+
|
223
149
|
def icon_keys
|
224
|
-
@icon_keys ||= case
|
225
|
-
when
|
150
|
+
@icon_keys ||= case device
|
151
|
+
when Device::IPHONE
|
226
152
|
[IPHONE_KEY]
|
227
|
-
when
|
153
|
+
when Device::IPAD
|
228
154
|
[IPAD_KEY]
|
229
|
-
when
|
155
|
+
when Device::UNIVERSAL
|
230
156
|
[IPHONE_KEY, IPAD_KEY]
|
231
157
|
end
|
232
158
|
end
|
data/lib/app_info/macos.rb
CHANGED
@@ -7,63 +7,42 @@ require 'cfpropertylist'
|
|
7
7
|
|
8
8
|
module AppInfo
|
9
9
|
# MacOS App parser
|
10
|
-
class Macos
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
#
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
def_delegators :mobileprovision, :team_name, :team_identifier,
|
42
|
-
:profile_name, :expired_date
|
43
|
-
|
44
|
-
def distribution_name
|
45
|
-
"#{profile_name} - #{team_name}" if profile_name && team_name
|
46
|
-
end
|
47
|
-
|
48
|
-
def release_type
|
49
|
-
if stored?
|
50
|
-
ExportType::APPSTORE
|
51
|
-
elsif mobileprovision?
|
52
|
-
ExportType::RELEASE
|
53
|
-
else
|
54
|
-
ExportType::DEBUG
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def stored?
|
59
|
-
File.exist?(store_path)
|
60
|
-
end
|
61
|
-
|
10
|
+
class Macos < Apple
|
11
|
+
# @!method min_sdk_version
|
12
|
+
# @see InfoPlist#min_sdk_version
|
13
|
+
def_delegators :info, :min_system_version
|
14
|
+
|
15
|
+
# Full icons metadata
|
16
|
+
# @param [Boolean] convert Convert .icons to .png format
|
17
|
+
# @example uncovert .icons
|
18
|
+
# macos.icons
|
19
|
+
# # => [
|
20
|
+
# # {
|
21
|
+
# # name: 'icon.icns',
|
22
|
+
# # file: '/path/to/icon.icns',
|
23
|
+
# # }
|
24
|
+
# # ]
|
25
|
+
#
|
26
|
+
# @example coverted .icons
|
27
|
+
# macos.icons(convert: true)
|
28
|
+
# # => [
|
29
|
+
# # {
|
30
|
+
# # name: 'converted_icon_32x32.png',
|
31
|
+
# # file: '/path/to/converted_icon_32x32.png',
|
32
|
+
# # dimensions: [32, 32]
|
33
|
+
# # },
|
34
|
+
# # {
|
35
|
+
# # name: 'converted_icon_120x120.png',
|
36
|
+
# # file: '/path/to/converted_icon_120x120.png',
|
37
|
+
# # dimensions: [120, 120]
|
38
|
+
# # },
|
39
|
+
# # ]
|
40
|
+
# @return [Array<Hash{Symbol => String, Array<Integer>}>] icons paths of icons
|
62
41
|
def icons(convert: true)
|
63
42
|
return unless icon_file
|
64
43
|
|
65
44
|
data = {
|
66
|
-
name: File.basename(icon_file),
|
45
|
+
name: ::File.basename(icon_file),
|
67
46
|
file: icon_file
|
68
47
|
}
|
69
48
|
|
@@ -71,63 +50,40 @@ module AppInfo
|
|
71
50
|
data
|
72
51
|
end
|
73
52
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
file = MachO.open(binary_path)
|
78
|
-
case file
|
79
|
-
when MachO::MachOFile
|
80
|
-
[file.cpusubtype]
|
81
|
-
else
|
82
|
-
file.machos.each_with_object([]) do |arch, obj|
|
83
|
-
obj << arch.cpusubtype
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
alias architectures archs
|
88
|
-
|
89
|
-
def hide_developer_certificates
|
90
|
-
mobileprovision.delete('DeveloperCertificates') if mobileprovision?
|
91
|
-
end
|
92
|
-
|
93
|
-
def mobileprovision
|
94
|
-
return unless mobileprovision?
|
95
|
-
|
96
|
-
@mobileprovision ||= MobileProvision.new(mobileprovision_path)
|
97
|
-
end
|
98
|
-
|
99
|
-
def mobileprovision?
|
100
|
-
File.exist?(mobileprovision_path)
|
53
|
+
# @return [Boolean]
|
54
|
+
def stored?
|
55
|
+
::File.exist?(store_path)
|
101
56
|
end
|
102
57
|
|
58
|
+
# @return [String]
|
103
59
|
def mobileprovision_path
|
104
|
-
@mobileprovision_path ||= File.join(app_path, 'Contents', 'embedded.provisionprofile')
|
60
|
+
@mobileprovision_path ||= ::File.join(app_path, 'Contents', 'embedded.provisionprofile')
|
105
61
|
end
|
106
62
|
|
63
|
+
# @return [String]
|
107
64
|
def store_path
|
108
|
-
@store_path ||= File.join(app_path, 'Contents', '_MASReceipt', 'receipt')
|
65
|
+
@store_path ||= ::File.join(app_path, 'Contents', '_MASReceipt', 'receipt')
|
109
66
|
end
|
110
67
|
|
68
|
+
# @return [String]
|
111
69
|
def binary_path
|
112
70
|
return @binary_path if @binary_path
|
113
71
|
|
114
|
-
base_path = File.join(app_path, 'Contents', 'MacOS')
|
72
|
+
base_path = ::File.join(app_path, 'Contents', 'MacOS')
|
115
73
|
binary = info['CFBundleExecutable']
|
116
|
-
return File.join(base_path, binary) if binary
|
74
|
+
return ::File.join(base_path, binary) if binary
|
117
75
|
|
118
|
-
@binary_path ||= Dir.glob(File.join(base_path, '*')).first
|
119
|
-
end
|
120
|
-
|
121
|
-
def info
|
122
|
-
@info ||= InfoPlist.new(info_path)
|
76
|
+
@binary_path ||= Dir.glob(::File.join(base_path, '*')).first
|
123
77
|
end
|
124
78
|
|
79
|
+
# @return [String]
|
125
80
|
def info_path
|
126
|
-
@info_path ||= File.join(app_path, 'Contents', 'Info.plist')
|
81
|
+
@info_path ||= ::File.join(app_path, 'Contents', 'Info.plist')
|
127
82
|
end
|
128
83
|
|
84
|
+
# @return [String]
|
129
85
|
def app_path
|
130
|
-
@app_path ||= Dir.glob(File.join(contents, '*.app')).first
|
86
|
+
@app_path ||= Dir.glob(::File.join(contents, '*.app')).first
|
131
87
|
end
|
132
88
|
|
133
89
|
def clear!
|
@@ -137,16 +93,12 @@ module AppInfo
|
|
137
93
|
|
138
94
|
@contents = nil
|
139
95
|
@app_path = nil
|
140
|
-
@
|
96
|
+
@binary_path = nil
|
141
97
|
@info_path = nil
|
142
98
|
@info = nil
|
143
99
|
@icons = nil
|
144
100
|
end
|
145
101
|
|
146
|
-
def contents
|
147
|
-
@contents ||= unarchive(@file, path: 'macos')
|
148
|
-
end
|
149
|
-
|
150
102
|
private
|
151
103
|
|
152
104
|
def icon_file
|
@@ -155,8 +107,8 @@ module AppInfo
|
|
155
107
|
info.icons.each do |key|
|
156
108
|
next unless value = info[key]
|
157
109
|
|
158
|
-
file = File.join(app_path, 'Contents', 'Resources', "#{value}.icns")
|
159
|
-
next unless File.file?(file)
|
110
|
+
file = ::File.join(app_path, 'Contents', 'Resources', "#{value}.icns")
|
111
|
+
next unless ::File.file?(file)
|
160
112
|
|
161
113
|
return @icon_file = file
|
162
114
|
end
|
@@ -173,14 +125,14 @@ module AppInfo
|
|
173
125
|
file = data[:file]
|
174
126
|
reader = Icns::Reader.new(file)
|
175
127
|
Icns::SIZE_TO_TYPE.each do |size, _|
|
176
|
-
dest_filename = "#{File.basename(file, '.icns')}_#{size}x#{size}.png"
|
177
|
-
dest_file = tempdir(File.join(File.dirname(file), dest_filename), prefix: 'converted')
|
128
|
+
dest_filename = "#{::File.basename(file, '.icns')}_#{size}x#{size}.png"
|
129
|
+
dest_file = tempdir(::File.join(::File.dirname(file), dest_filename), prefix: 'converted')
|
178
130
|
next unless icon_data = reader.image(size: size)
|
179
131
|
|
180
|
-
File.write(dest_file, icon_data, encoding: Encoding::BINARY)
|
132
|
+
::File.write(dest_file, icon_data, encoding: Encoding::BINARY)
|
181
133
|
|
182
134
|
data[:sets] << {
|
183
|
-
name: File.basename(dest_filename),
|
135
|
+
name: ::File.basename(dest_filename),
|
184
136
|
file: dest_file,
|
185
137
|
dimensions: ImageSize.path(dest_file).size
|
186
138
|
}
|