cocoapods-entitlements-statistics 0.0.1

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.
@@ -0,0 +1,317 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'core_ext/inflector'
4
+ require_relative 'core_ext/try'
5
+
6
+ require 'openssl'
7
+ require 'cfpropertylist'
8
+
9
+ module AppEntitlementsStatistics
10
+
11
+ # .mobileprovision file parser
12
+ class MobileProvision
13
+ def initialize(path)
14
+ @path = path
15
+ end
16
+
17
+ def name
18
+ mobileprovision.try(:[], 'Name')
19
+ end
20
+
21
+ def app_name
22
+ mobileprovision.try(:[], 'AppIDName')
23
+ end
24
+
25
+ def type
26
+ return :development if development?
27
+ return :adhoc if adhoc?
28
+ return :appstore if appstore?
29
+ return :enterprise if enterprise?
30
+ end
31
+
32
+ def platforms
33
+ return unless platforms = mobileprovision.try(:[], 'Platform')
34
+
35
+ platforms.map do |v|
36
+ v = 'macOS' if v == 'OSX'
37
+ v.downcase.to_sym
38
+ end
39
+ end
40
+
41
+ def platform
42
+ platforms[0]
43
+ end
44
+
45
+ def devices
46
+ mobileprovision.try(:[], 'ProvisionedDevices')
47
+ end
48
+
49
+ def team_identifier
50
+ mobileprovision.try(:[], 'TeamIdentifier')
51
+ end
52
+
53
+ def team_name
54
+ mobileprovision.try(:[], 'TeamName')
55
+ end
56
+
57
+ def profile_name
58
+ mobileprovision.try(:[], 'Name')
59
+ end
60
+
61
+ def created_date
62
+ mobileprovision.try(:[], 'CreationDate')
63
+ end
64
+
65
+ def expired_date
66
+ mobileprovision.try(:[], 'ExpirationDate')
67
+ end
68
+
69
+ def entitlements
70
+ mobileprovision.try(:[], 'Entitlements')
71
+ end
72
+
73
+ def developer_certs
74
+ certs = mobileprovision.try(:[], 'DeveloperCertificates')
75
+ return if certs.empty?
76
+
77
+ certs.each_with_object([]) do |cert, obj|
78
+ obj << DeveloperCertificate.new(cert)
79
+ end
80
+ end
81
+
82
+ # Detect is development type of mobileprovision
83
+ #
84
+ # related link: https://stackoverflow.com/questions/1003066/what-does-get-task-allow-do-in-xcode
85
+ def development?
86
+ case platform.downcase.to_sym
87
+ when :ios
88
+ entitlements['get-task-allow'] == true
89
+ when :macos
90
+ !devices.nil?
91
+ else
92
+ raise Error, "Not implement with platform: #{platform}"
93
+ end
94
+ end
95
+
96
+ # Detect app store type
97
+ #
98
+ # related link: https://developer.apple.com/library/archive/qa/qa1830/_index.html
99
+ def appstore?
100
+ case platform.downcase.to_sym
101
+ when :ios
102
+ !development? && entitlements.key?('beta-reports-active')
103
+ when :macos
104
+ !development?
105
+ else
106
+ raise Error, "Not implement with platform: #{platform}"
107
+ end
108
+ end
109
+
110
+ def adhoc?
111
+ return false if platform == :macos # macOS no need adhoc
112
+
113
+ !development? && !devices.nil?
114
+ end
115
+
116
+ def enterprise?
117
+ return false if platform == :macos # macOS no need adhoc
118
+
119
+ !development? && !adhoc? && !appstore?
120
+ end
121
+ alias inhouse? enterprise?
122
+
123
+ # Enabled Capabilites
124
+ #
125
+ # Related link: https://developer.apple.com/help/account/reference/supported-capabilities-ios
126
+ def enabled_capabilities
127
+ capabilities = Hash.new
128
+ entitlements.each do |key, value|
129
+ case key
130
+ when 'com.apple.developer.game-center'
131
+ capabilities['Game Center'] = {
132
+ key => value
133
+ }
134
+ when 'keychain-access-groups'
135
+ capabilities['Keychain sharing'] = {
136
+ key => value
137
+ }
138
+ when 'aps-environment'
139
+ capabilities['Push Notifications'] = {
140
+ key => value
141
+ }
142
+ when 'com.apple.developer.applesignin'
143
+ capabilities['Sign In with Apple'] = {
144
+ key => value
145
+ }
146
+ when 'com.apple.developer.siri'
147
+ capabilities['SiriKit'] = {
148
+ key => value
149
+ }
150
+ when 'com.apple.security.application-groups'
151
+ capabilities['App Groups'] = {
152
+ key => value
153
+ }
154
+ when 'com.apple.developer.associated-domains'
155
+ capabilities['Associated Domains'] = {
156
+ key => value
157
+ }
158
+ when 'com.apple.developer.default-data-protection'
159
+ capabilities['Data Protection'] = {
160
+ key => value
161
+ }
162
+ when 'com.apple.developer.networking.networkextension'
163
+ capabilities ['Network Extensions'] = {
164
+ key => value
165
+ }
166
+ when 'com.apple.developer.networking.vpn.api'
167
+ capabilities ['Personal VPN'] = {
168
+ key => value
169
+ }
170
+ when 'com.apple.developer.healthkit',
171
+ 'com.apple.developer.healthkit.access'
172
+ capabilities['HealthKit'] = {
173
+ 'com.apple.developer.healthkit' => entitlements['com.apple.developer.healthkit'],
174
+ 'com.apple.developer.healthkit.access' => entitlements['com.apple.developer.healthkit.access'],
175
+ } unless capabilities.include?('HealthKit')
176
+ when 'com.apple.developer.icloud-services',
177
+ 'com.apple.developer.icloud-container-identifiers'
178
+ capabilities['iCloud'] = {
179
+ 'com.apple.developer.icloud-services' => entitlements['com.apple.developer.icloud-services'],
180
+ 'com.apple.developer.icloud-container-identifiers' => entitlements['com.apple.developer.icloud-container-identifiers'],
181
+ } unless capabilities.include?('iCloud')
182
+ when 'com.apple.developer.in-app-payments'
183
+ capabilities['Apple Pay'] = {
184
+ key => value
185
+ }
186
+ when 'com.apple.developer.homekit'
187
+ capabilities['HomeKit'] = {
188
+ key => value
189
+ }
190
+ when 'com.apple.developer.user-fonts'
191
+ capabilities['Fonts'] = {
192
+ key => value
193
+ }
194
+ when 'com.apple.developer.pass-type-identifiers'
195
+ capabilities['Wallet'] = {
196
+ key => value
197
+ }
198
+ when 'inter-app-audio'
199
+ capabilities['Inter-App Audio'] = {
200
+ key => value
201
+ }
202
+ when 'com.apple.developer.networking.multipath'
203
+ capabilities['Multipath'] = {
204
+ key => value
205
+ }
206
+ when 'com.apple.developer.authentication-services.autofill-credential-provider'
207
+ capabilities['AutoFill Credential Provider'] = {
208
+ key => value
209
+ }
210
+ when 'com.apple.developer.networking.wifi-info'
211
+ capabilities['Access WiFi Information'] = {
212
+ key => value
213
+ }
214
+ when 'com.apple.external-accessory.wireless-configuration'
215
+ capabilities['Wireless Accessory Configuration'] = {
216
+ key => value
217
+ }
218
+ when 'com.apple.developer.kernel.extended-virtual-addressing'
219
+ capabilities['Extended Virtual Address Space'] = {
220
+ key => value
221
+ }
222
+ when 'com.apple.developer.nfc.readersession.formats'
223
+ capabilities['NFC Tag Reading'] = {
224
+ key => value
225
+ }
226
+ when 'com.apple.developer.ClassKit-environment'
227
+ capabilities['ClassKit'] = {
228
+ key => value
229
+ }
230
+ when 'com.apple.developer.networking.HotspotConfiguration'
231
+ capabilities['Hotspot'] = {
232
+ key => value
233
+ }
234
+ when 'com.apple.developer.devicecheck.appattest-environment'
235
+ capabilities['App Attest'] = {
236
+ key => value
237
+ }
238
+ when 'com.apple.developer.coremedia.hls.low-latency'
239
+ capabilities['Low Latency HLS'] = {
240
+ key => value
241
+ }
242
+ when 'com.apple.developer.associated-domains.mdm-managed'
243
+ capabilities['MDM Managed Associated Domains'] = {
244
+ key => value
245
+ }
246
+ end
247
+ end
248
+ capabilities
249
+ end
250
+
251
+
252
+ def [](key)
253
+ mobileprovision.try(:[], key.to_s)
254
+ end
255
+
256
+ def empty?
257
+ mobileprovision.nil?
258
+ end
259
+
260
+ def mobileprovision
261
+ return @mobileprovision = nil unless File.exist?(@path)
262
+
263
+ data = File.read(@path)
264
+ data = strip_plist_wrapper(data) unless bplist?(data)
265
+ list = CFPropertyList::List.new(data: data).value
266
+ @mobileprovision = CFPropertyList.native_types(list)
267
+ rescue CFFormatError
268
+ @mobileprovision = nil
269
+ end
270
+
271
+ def method_missing(method_name, *args, &block)
272
+ mobileprovision.try(:[], method_name.to_s.ai_camelcase) ||
273
+ mobileprovision.send(method_name) ||
274
+ super
275
+ end
276
+
277
+ def respond_to_missing?(method_name, *args)
278
+ mobileprovision.key?(method_name.to_s.ai_camelcase) ||
279
+ mobileprovision.respond_to?(method_name) ||
280
+ super
281
+ end
282
+
283
+ private
284
+
285
+ def bplist?(raw)
286
+ raw[0..5] == 'bplist'
287
+ end
288
+
289
+ def strip_plist_wrapper(raw)
290
+ end_tag = '</plist>'
291
+ start_point = raw.index('<?xml version=')
292
+ end_point = raw.index(end_tag) + end_tag.size - 1
293
+ raw[start_point..end_point]
294
+ end
295
+
296
+ # Developer Certificate
297
+ class DeveloperCertificate
298
+ attr_reader :raw
299
+
300
+ def initialize(data)
301
+ @raw = OpenSSL::X509::Certificate.new(data)
302
+ end
303
+
304
+ def name
305
+ @raw.subject.to_a.find { |name, _, _| name == 'CN' }[1].force_encoding('UTF-8')
306
+ end
307
+
308
+ def created_date
309
+ @raw.not_after
310
+ end
311
+
312
+ def expired_date
313
+ @raw.not_before
314
+ end
315
+ end
316
+ end
317
+ end
@@ -0,0 +1,3 @@
1
+ module CocoapodsEntitlementsStatistics
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,82 @@
1
+
2
+ require 'cocoapods'
3
+
4
+ module CocoapodsEntitlementsStatistics
5
+
6
+ class ProjectInfo
7
+ include Xcodeproj::Project::Object
8
+
9
+ attr_reader :project
10
+ attr_reader :load_success
11
+ attr_reader :tagret_name
12
+
13
+ def initialize
14
+ load_xcodeproj
15
+ end
16
+
17
+ def load_xcodeproj_success?
18
+ return @load_success
19
+ end
20
+
21
+ def load_xcodeproj
22
+ xcodeprojects = Pathname.glob('**/*.xcodeproj').reject { |path| path.to_s.start_with?('Pods/') }
23
+ if xcodeprojects.length == 1
24
+ @tagret_name = Pathname(xcodeprojects.first).basename('.xcodeproj')
25
+ @project = Xcodeproj::Project.open(xcodeprojects.first.realpath)
26
+ @load_success = true
27
+ else
28
+ @load_success = false
29
+ error_message = xcodeprojects.length > 1 ? 'found too many' : "couldn't find any"
30
+ projects = xcodeprojects.map(&:basename).join(' ')
31
+ Pod::UI.notice 'CocoaPods-Keys ' + error_message + ' Xcode projects (' + projects + '). Please give a name for this project.'
32
+ end
33
+ end
34
+
35
+ def info_plist
36
+ info_plists = Pathname.glob('**/info.plist').reject { |path| !path.to_s.start_with?("#{@tagret_name}/") }
37
+ info_plist_path = info_plists.length > 0 ? info_plists.first.realpath : ""
38
+ info_plist_path
39
+ end
40
+
41
+ def entitlements_plist
42
+ entitlements_files = Pathname.glob("**/#{@tagret_name}.entitlements").reject { |path| !path.to_s.start_with?("#{@tagret_name}/") }
43
+ entitlements_path = entitlements_files.length > 0 ? entitlements_files.first.realpath : ""
44
+ entitlements_path
45
+ end
46
+
47
+ def project_release_config
48
+ return @release_configurations unless @release_configurations.nil?
49
+ @project.targets[0].build_configurations.each do |config|
50
+ if config.name == 'Release'
51
+ @release_configurations = config
52
+ end
53
+ end
54
+ @release_configurations
55
+ end
56
+
57
+ def marketing_version
58
+ return nil unless !project_release_config.nil?
59
+ marketing_version = project_release_config.build_settings["MARKETING_VERSION"]
60
+ marketing_version
61
+ end
62
+
63
+ def product_bundle_identifier
64
+ return nil unless !project_release_config.nil?
65
+ product_bundle_identifier = project_release_config.build_settings["PRODUCT_BUNDLE_IDENTIFIER"]
66
+ product_bundle_identifier
67
+ end
68
+
69
+ def system_capabilities
70
+ target_attr_hash = @project.root_object.attributes["TargetAttributes"]
71
+ system_capabilities = Hash.new
72
+ target_attr_hash.each do |_,value|
73
+ if value.class == Hash && value.keys.include?("SystemCapabilities")
74
+ system_capabilities = value["SystemCapabilities"]
75
+ end
76
+ end
77
+ system_capabilities
78
+ end
79
+
80
+ end
81
+
82
+ end
@@ -0,0 +1 @@
1
+ require 'cocoapods-entitlements-statistics/gem_version'
@@ -0,0 +1,34 @@
1
+ require "cocoapods-entitlements-statistics/app_entitlements_statistics/extracter"
2
+ require "cocoapods-entitlements-statistics/app_entitlements_statistics/analyze"
3
+ require "cocoapods-entitlements-statistics/project_info"
4
+ require 'cocoapods'
5
+
6
+ module CocoapodsEntitlementsStatistics
7
+
8
+ Pod::HooksManager.register('cocoapods-entitlements-statistics', :post_install) do |context,options|
9
+
10
+ project_info = ProjectInfo.new()
11
+
12
+ if project_info.load_xcodeproj_success?
13
+ args = {
14
+ "cur_version" => project_info.marketing_version,
15
+ "product_bundle_identifier" => project_info.product_bundle_identifier,
16
+ "info_plist_path" => project_info.info_plist,
17
+ "entitlements_path" => project_info.entitlements_plist,
18
+ "system_capabilities" => project_info.system_capabilities
19
+ }
20
+
21
+ # extract_update 、analyze
22
+ extracter = AppEntitlementsStatistics::Extracter.new(args)
23
+ extracter.extract_update
24
+
25
+ report_path = (!options.nil? && options.has_key?("report_path")) ? options[:report_path] : nil
26
+ analyzer = AppEntitlementsStatistics::Analyzer.new(project_info.product_bundle_identifier, report_path: report_path )
27
+
28
+ report_real_path = analyzer.analyze
29
+ Pod::UI.notice "Entitlements Statistics Report : #{File.expand_path(report_real_path)}" unless report_real_path.empty?
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,12 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+
3
+ module Pod
4
+ describe Command::Statistics do
5
+ describe 'CLAide' do
6
+ it 'registers it self' do
7
+ Command.parse(%w{ statistics }).should.be.instance_of Command::Statistics
8
+ end
9
+ end
10
+ end
11
+ end
12
+
@@ -0,0 +1,50 @@
1
+ require 'pathname'
2
+ ROOT = Pathname.new(File.expand_path('../../', __FILE__))
3
+ $:.unshift((ROOT + 'lib').to_s)
4
+ $:.unshift((ROOT + 'spec').to_s)
5
+
6
+ require 'bundler/setup'
7
+ require 'bacon'
8
+ require 'mocha-on-bacon'
9
+ require 'pretty_bacon'
10
+ require 'pathname'
11
+ require 'cocoapods'
12
+
13
+ Mocha::Configuration.prevent(:stubbing_non_existent_method)
14
+
15
+ require 'cocoapods_plugin'
16
+
17
+ #-----------------------------------------------------------------------------#
18
+
19
+ module Pod
20
+
21
+ # Disable the wrapping so the output is deterministic in the tests.
22
+ #
23
+ UI.disable_wrap = true
24
+
25
+ # Redirects the messages to an internal store.
26
+ #
27
+ module UI
28
+ @output = ''
29
+ @warnings = ''
30
+
31
+ class << self
32
+ attr_accessor :output
33
+ attr_accessor :warnings
34
+
35
+ def puts(message = '')
36
+ @output << "#{message}\n"
37
+ end
38
+
39
+ def warn(message = '', actions = [])
40
+ @warnings << "#{message}\n"
41
+ end
42
+
43
+ def print(message)
44
+ @output << message
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ #-----------------------------------------------------------------------------#
metadata ADDED
@@ -0,0 +1,161 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cocoapods-entitlements-statistics
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - bin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-04-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: yaml
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: crimp
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: CFPropertyList
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "<"
46
+ - !ruby/object:Gem::Version
47
+ version: 3.1.0
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: 2.3.4
51
+ type: :runtime
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - "<"
56
+ - !ruby/object:Gem::Version
57
+ version: 3.1.0
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: 2.3.4
61
+ - !ruby/object:Gem::Dependency
62
+ name: bundler
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '1.12'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '1.12'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rubyzip
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '1.2'
82
+ - - "<"
83
+ - !ruby/object:Gem::Version
84
+ version: '3.0'
85
+ type: :runtime
86
+ prerelease: false
87
+ version_requirements: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '1.2'
92
+ - - "<"
93
+ - !ruby/object:Gem::Version
94
+ version: '3.0'
95
+ - !ruby/object:Gem::Dependency
96
+ name: rake
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ description: A short description of cocoapods-entitlements-statistics.
110
+ email:
111
+ - tang.bin@olaola.chat
112
+ executables: []
113
+ extensions: []
114
+ extra_rdoc_files: []
115
+ files:
116
+ - ".gitignore"
117
+ - Gemfile
118
+ - LICENSE.txt
119
+ - README.md
120
+ - Rakefile
121
+ - cocoapods-entitlements-statistics.gemspec
122
+ - lib/cocoapods-entitlements-statistics.rb
123
+ - lib/cocoapods-entitlements-statistics/app_entitlements_statistics/analyze.rb
124
+ - lib/cocoapods-entitlements-statistics/app_entitlements_statistics/core_ext/inflector.rb
125
+ - lib/cocoapods-entitlements-statistics/app_entitlements_statistics/core_ext/try.rb
126
+ - lib/cocoapods-entitlements-statistics/app_entitlements_statistics/entitlements.rb
127
+ - lib/cocoapods-entitlements-statistics/app_entitlements_statistics/extracter.rb
128
+ - lib/cocoapods-entitlements-statistics/app_entitlements_statistics/helper.rb
129
+ - lib/cocoapods-entitlements-statistics/app_entitlements_statistics/info_plist.rb
130
+ - lib/cocoapods-entitlements-statistics/app_entitlements_statistics/mobile_provision.rb
131
+ - lib/cocoapods-entitlements-statistics/gem_version.rb
132
+ - lib/cocoapods-entitlements-statistics/project_info.rb
133
+ - lib/cocoapods_plugin.rb
134
+ - spec/command/statistics_spec.rb
135
+ - spec/spec_helper.rb
136
+ homepage: https://github.com/EXAMPLE/cocoapods-entitlements-statistics
137
+ licenses:
138
+ - MIT
139
+ metadata: {}
140
+ post_install_message:
141
+ rdoc_options: []
142
+ require_paths:
143
+ - lib
144
+ required_ruby_version: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ version: '0'
149
+ required_rubygems_version: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
154
+ requirements: []
155
+ rubygems_version: 3.2.3
156
+ signing_key:
157
+ specification_version: 4
158
+ summary: A longer description of cocoapods-entitlements-statistics.
159
+ test_files:
160
+ - spec/command/statistics_spec.rb
161
+ - spec/spec_helper.rb