app-info 2.8.2 → 3.0.0

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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +12 -7
  3. data/.github/workflows/ci.yml +7 -5
  4. data/.github/workflows/create_release.yml +15 -0
  5. data/.rubocop.yml +33 -11
  6. data/CHANGELOG.md +107 -1
  7. data/Gemfile +10 -5
  8. data/README.md +82 -15
  9. data/Rakefile +11 -0
  10. data/app_info.gemspec +14 -5
  11. data/lib/app_info/aab.rb +76 -110
  12. data/lib/app_info/android/signature.rb +114 -0
  13. data/lib/app_info/android/signatures/base.rb +53 -0
  14. data/lib/app_info/android/signatures/info.rb +158 -0
  15. data/lib/app_info/android/signatures/v1.rb +63 -0
  16. data/lib/app_info/android/signatures/v2.rb +121 -0
  17. data/lib/app_info/android/signatures/v3.rb +131 -0
  18. data/lib/app_info/android/signatures/v4.rb +18 -0
  19. data/lib/app_info/android.rb +181 -0
  20. data/lib/app_info/apk.rb +77 -112
  21. data/lib/app_info/apple.rb +192 -0
  22. data/lib/app_info/certificate.rb +176 -0
  23. data/lib/app_info/const.rb +76 -0
  24. data/lib/app_info/core_ext/object/try.rb +3 -1
  25. data/lib/app_info/core_ext/string/inflector.rb +2 -0
  26. data/lib/app_info/dsym/debug_info.rb +81 -0
  27. data/lib/app_info/dsym/macho.rb +62 -0
  28. data/lib/app_info/dsym.rb +35 -135
  29. data/lib/app_info/error.rb +3 -1
  30. data/lib/app_info/file.rb +49 -0
  31. data/lib/app_info/helper/archive.rb +37 -0
  32. data/lib/app_info/helper/file_size.rb +25 -0
  33. data/lib/app_info/helper/generate_class.rb +29 -0
  34. data/lib/app_info/helper/protobuf.rb +12 -0
  35. data/lib/app_info/helper/signatures.rb +229 -0
  36. data/lib/app_info/helper.rb +5 -128
  37. data/lib/app_info/info_plist.rb +66 -29
  38. data/lib/app_info/ipa/framework.rb +4 -4
  39. data/lib/app_info/ipa.rb +61 -135
  40. data/lib/app_info/macos.rb +54 -102
  41. data/lib/app_info/mobile_provision.rb +66 -48
  42. data/lib/app_info/pe.rb +322 -0
  43. data/lib/app_info/png_uncrush.rb +25 -5
  44. data/lib/app_info/proguard.rb +39 -22
  45. data/lib/app_info/protobuf/manifest.rb +22 -11
  46. data/lib/app_info/protobuf/models/Configuration_pb.rb +1 -0
  47. data/lib/app_info/protobuf/models/README.md +8 -1
  48. data/lib/app_info/protobuf/models/Resources.proto +51 -0
  49. data/lib/app_info/protobuf/models/Resources_pb.rb +42 -0
  50. data/lib/app_info/protobuf/resources.rb +5 -5
  51. data/lib/app_info/version.rb +1 -1
  52. data/lib/app_info.rb +93 -43
  53. metadata +57 -37
@@ -7,63 +7,42 @@ require 'cfpropertylist'
7
7
 
8
8
  module AppInfo
9
9
  # MacOS App parser
10
- class Macos
11
- include Helper::HumanFileSize
12
- include Helper::Archive
13
- extend Forwardable
14
-
15
- attr_reader :file
16
-
17
- # macOS Export types
18
- module ExportType
19
- DEBUG = 'Debug'
20
- RELEASE = 'Release'
21
- APPSTORE = 'AppStore'
22
- end
23
-
24
- def initialize(file)
25
- @file = file
26
- end
27
-
28
- def size(human_size: false)
29
- file_to_human_size(@file, human_size: human_size)
30
- end
31
-
32
- def os
33
- Platform::MACOS
34
- end
35
- alias file_type os
36
-
37
- def_delegators :info, :macos?, :iphone?, :ipad?, :universal?, :build_version, :name,
38
- :release_version, :identifier, :bundle_id, :display_name,
39
- :bundle_name, :min_system_version, :min_os_version, :device_type
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
- def archs
75
- return unless File.exist?(binary_path)
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
- @binrary_path = nil
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
  }
@@ -4,20 +4,37 @@ require 'openssl'
4
4
  require 'cfpropertylist'
5
5
 
6
6
  module AppInfo
7
- # .mobileprovision file parser
8
- class MobileProvision
9
- def initialize(path)
10
- @path = path
7
+ # Apple code signing: provisioning profile parser
8
+ # @see https://developer.apple.com/documentation/technotes/tn3125-inside-code-signing-provisioning-profiles
9
+ class MobileProvision < File
10
+ # @return [Symbol] {Manufacturer}
11
+ def manufacturer
12
+ Manufacturer::APPLE
11
13
  end
12
14
 
15
+ # @return [Symbol] {Platform}
16
+ def platform
17
+ case platforms[0]
18
+ when :macos
19
+ Platform::MACOS
20
+ when :ios
21
+ Platform::IOS
22
+ else
23
+ raise NotImplementedError, "Unkonwn platform: #{platforms[0]}"
24
+ end
25
+ end
26
+
27
+ # @return [String, nil]
13
28
  def name
14
29
  mobileprovision.try(:[], 'Name')
15
30
  end
16
31
 
32
+ # @return [String, nil]
17
33
  def app_name
18
34
  mobileprovision.try(:[], 'AppIDName')
19
35
  end
20
36
 
37
+ # @return [Symbol, nil]
21
38
  def type
22
39
  return :development if development?
23
40
  return :adhoc if adhoc?
@@ -25,6 +42,7 @@ module AppInfo
25
42
  return :enterprise if enterprise?
26
43
  end
27
44
 
45
+ # @return [Array<Symbol>]
28
46
  def platforms
29
47
  return unless platforms = mobileprovision.try(:[], 'Platform')
30
48
 
@@ -34,83 +52,100 @@ module AppInfo
34
52
  end
35
53
  end
36
54
 
37
- def platform
38
- platforms[0]
39
- end
40
-
55
+ # @return [Array<String>, nil]
41
56
  def devices
42
57
  mobileprovision.try(:[], 'ProvisionedDevices')
43
58
  end
44
59
 
60
+ # @return [String, nil]
45
61
  def team_identifier
46
62
  mobileprovision.try(:[], 'TeamIdentifier')
47
63
  end
48
64
 
65
+ # @return [String, nil]
49
66
  def team_name
50
67
  mobileprovision.try(:[], 'TeamName')
51
68
  end
52
69
 
70
+ # @return [String, nil]
53
71
  def profile_name
54
72
  mobileprovision.try(:[], 'Name')
55
73
  end
56
74
 
75
+ # @return [String, nil]
57
76
  def created_date
58
77
  mobileprovision.try(:[], 'CreationDate')
59
78
  end
60
79
 
80
+ # @return [String, nil]
61
81
  def expired_date
62
82
  mobileprovision.try(:[], 'ExpirationDate')
63
83
  end
64
84
 
85
+ # @return [Array<String>, nil]
65
86
  def entitlements
66
87
  mobileprovision.try(:[], 'Entitlements')
67
88
  end
68
89
 
90
+ # return developer certificates.
91
+ #
92
+ # @deprecated Use {#certificates} instead of this method.
69
93
  def developer_certs
94
+ certificates
95
+ end
96
+
97
+ # return developer certificates.
98
+ #
99
+ # @return [Array<Certificate>]
100
+ def certificates
70
101
  certs = mobileprovision.try(:[], 'DeveloperCertificates')
71
102
  return if certs.empty?
72
103
 
73
- certs.each_with_object([]) do |cert, obj|
74
- obj << DeveloperCertificate.new(cert)
104
+ certs.each_with_object([]) do |cert_data, obj|
105
+ obj << Certificate.parse(cert_data)
75
106
  end
76
107
  end
77
108
 
78
109
  # Detect is development type of mobileprovision
79
110
  #
80
- # related link: https://stackoverflow.com/questions/1003066/what-does-get-task-allow-do-in-xcode
111
+ # @see https://stackoverflow.com/questions/1003066/what-does-get-task-allow-do-in-xcode
112
+ # @return [Boolea]
81
113
  def development?
82
- case platform.downcase.to_sym
83
- when :ios
114
+ case platform
115
+ when Platform::IOS
84
116
  entitlements['get-task-allow'] == true
85
- when :macos
117
+ when Platform::MACOS
86
118
  !devices.nil?
87
119
  else
88
- raise Error, "Not implement with platform: #{platform}"
120
+ raise NotImplementedError, "Unknown platform: #{platform}"
89
121
  end
90
122
  end
91
123
 
92
124
  # Detect app store type
93
125
  #
94
- # related link: https://developer.apple.com/library/archive/qa/qa1830/_index.html
126
+ # @see https://developer.apple.com/library/archive/qa/qa1830/_index.html
127
+ # @return [Boolea]
95
128
  def appstore?
96
- case platform.downcase.to_sym
97
- when :ios
129
+ case platform
130
+ when Platform::IOS
98
131
  !development? && entitlements.key?('beta-reports-active')
99
- when :macos
132
+ when Platform::MACOS
100
133
  !development?
101
134
  else
102
- raise Error, "Not implement with platform: #{platform}"
135
+ raise NotImplementedError, "Unknown platform: #{platform}"
103
136
  end
104
137
  end
105
138
 
139
+ # @return [Boolea]
106
140
  def adhoc?
107
- return false if platform == :macos # macOS no need adhoc
141
+ return false if platform == Platform::MACOS # macOS no need adhoc
108
142
 
109
143
  !development? && !devices.nil?
110
144
  end
111
145
 
146
+ # @return [Boolea]
112
147
  def enterprise?
113
- return false if platform == :macos # macOS no need adhoc
148
+ return false if platform == Platform::MACOS # macOS no need adhoc
114
149
 
115
150
  !development? && !adhoc? && !appstore?
116
151
  end
@@ -118,7 +153,8 @@ module AppInfo
118
153
 
119
154
  # Enabled Capabilites
120
155
  #
121
- # Related link: https://developer.apple.com/support/app-capabilities/
156
+ # @see https://developer.apple.com/support/app-capabilities/
157
+ # @return [Array<String>]
122
158
  def enabled_capabilities
123
159
  capabilities = []
124
160
  capabilities << 'In-App Purchase' << 'GameKit' if adhoc? || appstore?
@@ -142,10 +178,10 @@ module AppInfo
142
178
  when 'com.apple.developer.networking.vpn.api'
143
179
  capabilities << 'Personal VPN'
144
180
  when 'com.apple.developer.healthkit',
145
- 'com.apple.developer.healthkit.access'
181
+ 'com.apple.developer.healthkit.access'
146
182
  capabilities << 'HealthKit' unless capabilities.include?('HealthKit')
147
183
  when 'com.apple.developer.icloud-services',
148
- 'com.apple.developer.icloud-container-identifiers'
184
+ 'com.apple.developer.icloud-container-identifiers'
149
185
  capabilities << 'iCloud' unless capabilities.include?('iCloud')
150
186
  when 'com.apple.developer.in-app-payments'
151
187
  capabilities << 'Apple Pay'
@@ -192,18 +228,21 @@ module AppInfo
192
228
  capabilities
193
229
  end
194
230
 
231
+ # @return [String, nil]
195
232
  def [](key)
196
233
  mobileprovision.try(:[], key.to_s)
197
234
  end
198
235
 
236
+ # @return [Boolea]
199
237
  def empty?
200
238
  mobileprovision.nil?
201
239
  end
202
240
 
241
+ # @return [CFPropertyList]
203
242
  def mobileprovision
204
- return @mobileprovision = nil unless File.exist?(@path)
243
+ return @mobileprovision = nil unless ::File.exist?(@file)
205
244
 
206
- data = File.read(@path)
245
+ data = ::File.read(@file)
207
246
  data = strip_plist_wrapper(data) unless bplist?(data)
208
247
  list = CFPropertyList::List.new(data: data).value
209
248
  @mobileprovision = CFPropertyList.native_types(list)
@@ -235,26 +274,5 @@ module AppInfo
235
274
  end_point = raw.index(end_tag) + end_tag.size - 1
236
275
  raw[start_point..end_point]
237
276
  end
238
-
239
- # Developer Certificate
240
- class DeveloperCertificate
241
- attr_reader :raw
242
-
243
- def initialize(data)
244
- @raw = OpenSSL::X509::Certificate.new(data)
245
- end
246
-
247
- def name
248
- @raw.subject.to_a.find { |name, _, _| name == 'CN' }[1].force_encoding('UTF-8')
249
- end
250
-
251
- def created_date
252
- @raw.not_after
253
- end
254
-
255
- def expired_date
256
- @raw.not_before
257
- end
258
- end
259
277
  end
260
278
  end