pprof 0.3.9 → 0.5.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.
- checksums.yaml +5 -5
- data/bin/pprof +48 -41
- data/lib/pprof/entitlements.rb +6 -3
- data/lib/pprof/output_formatter.rb +135 -45
- data/lib/pprof/provisioning_profile.rb +22 -20
- data/lib/pprof/version.rb +3 -1
- data/lib/pprof.rb +2 -0
- metadata +9 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: f7691660bb69c7c6f6f4fedb4722dc0e647183378e5aa79cfa0ce2a80f7e4cef
|
|
4
|
+
data.tar.gz: ae54346cc1d76c10a89468b605a698727ff38909f683bed7ff8a6d779c6605b0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9b2490137336562c025610bbfc7de8b0fe1a177005b9a7c7cfea21310fedb3f77164745303dc08b1816d3e8dd5a0f7a5aaa71373a71c784921982d36da4e876f
|
|
7
|
+
data.tar.gz: 22665cd4ba32a996577a8ed8099348f29193fb88589a32b1faaf8ca03298a971902373d23cdad568c59a1b6e23f59f81e3c0a3e543e8173c18ddd71f098379a5
|
data/bin/pprof
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
|
|
3
4
|
require 'pprof'
|
|
4
5
|
require 'optparse'
|
|
@@ -7,7 +8,7 @@ require 'optparse'
|
|
|
7
8
|
# Or a standard match if the string is bare.
|
|
8
9
|
#
|
|
9
10
|
def matcher(string)
|
|
10
|
-
m = string.match(%r
|
|
11
|
+
m = string.match(%r{^/(.*)/(.*)$})
|
|
11
12
|
if m.nil?
|
|
12
13
|
Regexp.new(Regexp.escape(string))
|
|
13
14
|
else
|
|
@@ -19,10 +20,11 @@ end
|
|
|
19
20
|
|
|
20
21
|
filters = {}
|
|
21
22
|
options = {}
|
|
22
|
-
list_options = { :
|
|
23
|
+
list_options = { mode: :table }
|
|
23
24
|
|
|
25
|
+
# rubocop:disable Metrics/BlockLength
|
|
24
26
|
parser = OptionParser.new do |opts|
|
|
25
|
-
opts.banner = <<-BANNER.gsub(/^ *\|/,'')
|
|
27
|
+
opts.banner = <<-BANNER.gsub(/^ *\|/, '')
|
|
26
28
|
|Usage:
|
|
27
29
|
| pprof [print_options] (PATH|UUID)
|
|
28
30
|
| pprof [list_options] [filters]
|
|
@@ -40,125 +42,130 @@ parser = OptionParser.new do |opts|
|
|
|
40
42
|
| - To remove all expired certificates, you can use `#{opts.program_name} -e0 | xargs -0 rm`
|
|
41
43
|
BANNER
|
|
42
44
|
|
|
43
|
-
opts.separator
|
|
44
|
-
opts.separator
|
|
45
|
+
opts.separator ''
|
|
46
|
+
opts.separator 'Print options (when file given)'
|
|
45
47
|
|
|
46
|
-
opts.on(
|
|
48
|
+
opts.on('-i', '--info', 'Print general info (default)') do
|
|
47
49
|
options[:info] = true
|
|
48
50
|
end
|
|
49
|
-
opts.on(
|
|
51
|
+
opts.on('-c', '--certs', 'Print certificates') do
|
|
50
52
|
options[:certs] = true
|
|
51
53
|
end
|
|
52
|
-
opts.on(
|
|
54
|
+
opts.on('-d', '--devices', 'Print provisioned devices') do
|
|
53
55
|
options[:devices] = true
|
|
54
56
|
end
|
|
55
57
|
|
|
56
|
-
opts.separator
|
|
57
|
-
opts.separator
|
|
58
|
+
opts.separator ''
|
|
59
|
+
opts.separator 'List options (when no file given)'
|
|
58
60
|
|
|
59
|
-
opts.on(
|
|
61
|
+
opts.on('-l', '--list', 'Print only the UUIDs, one per line (instead of an ASCII table)') do
|
|
60
62
|
list_options[:mode] = :list
|
|
61
63
|
end
|
|
62
|
-
opts.on(
|
|
64
|
+
opts.on('-p', '--path', 'Print only the paths, one per line (instead of an ASCII table)') do
|
|
63
65
|
list_options[:mode] = :path
|
|
64
66
|
end
|
|
65
|
-
opts.on(
|
|
67
|
+
opts.on('-j', '--json', 'Print the output information as a JSON') do
|
|
68
|
+
list_options[:mode] = :json
|
|
69
|
+
end
|
|
70
|
+
opts.on('-0', '--print0', 'Separate each found entry by \\0, to be used by `xargs -0`') do
|
|
66
71
|
list_options[:zero] = true
|
|
67
72
|
end
|
|
68
73
|
|
|
69
|
-
opts.separator
|
|
70
|
-
opts.separator
|
|
74
|
+
opts.separator ''
|
|
75
|
+
opts.separator 'Filters (when no file given)'
|
|
71
76
|
|
|
72
|
-
opts.on(
|
|
77
|
+
opts.on('--name NAME', 'Filter by name') do |name|
|
|
73
78
|
filters[:name] = matcher(name)
|
|
74
79
|
end
|
|
75
80
|
|
|
76
|
-
opts.on(
|
|
81
|
+
opts.on('--appid-name APPID', 'Filter by App ID Name') do |appid_name|
|
|
77
82
|
filters[:appid_name] = matcher(appid_name)
|
|
78
83
|
end
|
|
79
84
|
|
|
80
|
-
opts.on(
|
|
85
|
+
opts.on('--appid APPID', 'Filter by App ID') do |appid|
|
|
81
86
|
filters[:appid] = matcher(appid)
|
|
82
87
|
end
|
|
83
88
|
|
|
84
|
-
opts.on(
|
|
89
|
+
opts.on('--uuid UUID', 'Filter by UUID') do |uuid|
|
|
85
90
|
filters[:uuid] = matcher(uuid)
|
|
86
91
|
end
|
|
87
92
|
|
|
88
|
-
opts.on(
|
|
93
|
+
opts.on('--team TEAM', 'Filter by team name or ID') do |team|
|
|
89
94
|
filters[:team] = matcher(team)
|
|
90
95
|
end
|
|
91
96
|
|
|
92
|
-
opts.on(
|
|
97
|
+
opts.on('--[no-]exp', 'Only profiles (not) expired') do |flag|
|
|
93
98
|
filters[:exp] = flag
|
|
94
99
|
end
|
|
95
100
|
|
|
96
|
-
opts.on(
|
|
101
|
+
opts.on('--[no-]has-devices', 'Filter by profiles having (no) provisioned devices') do |d|
|
|
97
102
|
filters[:has_devices] = d
|
|
98
103
|
end
|
|
99
104
|
|
|
100
|
-
opts.on(
|
|
105
|
+
opts.on('--[no-]all-devices', 'Filter by profiles (not) provisioning all devices') do |d|
|
|
101
106
|
filters[:all_devices] = d
|
|
102
107
|
end
|
|
103
108
|
|
|
104
|
-
opts.on(
|
|
109
|
+
opts.on('--aps [ENV]', 'Only profiles having Push entitlements (or a specific aps env)') do |env|
|
|
105
110
|
filters[:aps_env] = env.nil? ? true : matcher(env)
|
|
106
111
|
end
|
|
107
112
|
|
|
113
|
+
opts.separator ''
|
|
114
|
+
opts.separator 'Misc'
|
|
108
115
|
|
|
109
|
-
opts.
|
|
110
|
-
opts.separator "Misc"
|
|
111
|
-
|
|
112
|
-
opts.on_tail("-h", "--help", "Show this message") do
|
|
116
|
+
opts.on_tail('-h', '--help', 'Show this message') do
|
|
113
117
|
puts opts
|
|
114
118
|
exit
|
|
115
119
|
end
|
|
116
120
|
|
|
117
|
-
opts.on_tail(
|
|
121
|
+
opts.on_tail('-v', '--version', 'Show version') do
|
|
118
122
|
puts PProf::VERSION
|
|
119
123
|
exit
|
|
120
124
|
end
|
|
121
125
|
end
|
|
126
|
+
# rubocop:enable Metrics/BlockLength
|
|
122
127
|
|
|
123
128
|
# Parse the options, catching parse errors nicely
|
|
124
129
|
begin
|
|
125
130
|
parser.parse!
|
|
126
|
-
rescue OptionParser::InvalidOption =>
|
|
127
|
-
puts
|
|
128
|
-
puts parser.help
|
|
131
|
+
rescue OptionParser::InvalidOption => e
|
|
132
|
+
puts e
|
|
133
|
+
puts parser.help
|
|
129
134
|
exit 1
|
|
130
135
|
end
|
|
131
136
|
|
|
132
137
|
# Don't mix filters and options together, that doesn't make sense
|
|
133
138
|
|
|
134
139
|
unless filters.empty? || ARGV.empty?
|
|
135
|
-
puts
|
|
140
|
+
puts 'You should use either filter flags to filter the whole list, or an specific path, not both.'
|
|
136
141
|
puts parser # Usage
|
|
137
142
|
exit
|
|
138
143
|
end
|
|
139
144
|
|
|
140
145
|
unless options.empty? || !ARGV.empty?
|
|
141
|
-
puts
|
|
146
|
+
puts 'You should use option flags only when providing an specific path.'
|
|
142
147
|
puts parser # Usage
|
|
143
148
|
exit
|
|
144
149
|
end
|
|
145
150
|
|
|
146
151
|
# Call the appropriate action
|
|
147
152
|
|
|
153
|
+
o = PProf::OutputFormatter.new
|
|
148
154
|
if ARGV.empty?
|
|
149
155
|
# Print list of matching profiles
|
|
150
|
-
o = PProf::OutputFormatter.new
|
|
151
156
|
list_options[:mode] = :path if list_options[:zero] && list_options[:mode] == :table
|
|
152
157
|
o.print_filtered_list(PProf::ProvisioningProfile::DEFAULT_DIR, filters, list_options)
|
|
153
158
|
else
|
|
154
|
-
# Print info about given profile path/UUID
|
|
155
|
-
o = PProf::OutputFormatter.new
|
|
156
159
|
begin
|
|
160
|
+
# Print info about given profile path/UUID
|
|
157
161
|
p = PProf::ProvisioningProfile.new(ARGV[0])
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
+
options = { info: true } if options.empty?
|
|
163
|
+
if list_options[:mode] == :json
|
|
164
|
+
o.print_json(p, options)
|
|
165
|
+
else
|
|
166
|
+
o.print_info(p, options)
|
|
167
|
+
end
|
|
168
|
+
rescue StandardError => e
|
|
162
169
|
o.print_error(e, ARGV[0])
|
|
163
170
|
end
|
|
164
171
|
end
|
data/lib/pprof/entitlements.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
# Module for the pprof tool to manipulate Provisioning Profiles
|
|
2
4
|
module PProf
|
|
3
5
|
# Represents the list of entitlements in a Provisioning Profile
|
|
@@ -23,7 +25,7 @@ module PProf
|
|
|
23
25
|
# True if we can attach a debugger to the executable, false if not.
|
|
24
26
|
#
|
|
25
27
|
# @return [Bool]
|
|
26
|
-
def get_task_allow
|
|
28
|
+
def get_task_allow # rubocop:disable Naming/AccessorMethodName
|
|
27
29
|
@dict['get-task-allow']
|
|
28
30
|
end
|
|
29
31
|
|
|
@@ -98,9 +100,10 @@ module PProf
|
|
|
98
100
|
# @param [#to_s] key
|
|
99
101
|
# The key to check
|
|
100
102
|
#
|
|
101
|
-
def
|
|
102
|
-
@dict.
|
|
103
|
+
def key?(key)
|
|
104
|
+
@dict.key?(key.to_s)
|
|
103
105
|
end
|
|
106
|
+
alias has_key? key?
|
|
104
107
|
|
|
105
108
|
# The list of all entitlement keys, as String
|
|
106
109
|
#
|
|
@@ -1,7 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
|
|
1
5
|
# Module for the pprof tool to manipulate Provisioning Profiles
|
|
2
6
|
module PProf
|
|
3
7
|
# A helper tool to pretty-print Provisioning Profile informations
|
|
4
8
|
class OutputFormatter
|
|
9
|
+
# List of properties of a `PProf::ProvisioningProfile` to print when using the `-i` flag
|
|
10
|
+
MAIN_PROFILE_KEYS = %i[name uuid app_id_name app_id_prefix creation_date expiration_date ttl team_ids team_name]
|
|
11
|
+
|
|
5
12
|
# Initialize a new OutputFormatter
|
|
6
13
|
#
|
|
7
14
|
# @param [IO] output
|
|
@@ -27,14 +34,16 @@ module PProf
|
|
|
27
34
|
# @param [String...] cols
|
|
28
35
|
# The content of each column of the row to add
|
|
29
36
|
def row(*cols)
|
|
30
|
-
|
|
37
|
+
justified_cols = cols.zip(@widths).map do |c, w|
|
|
31
38
|
(c || '<nil>').to_s.ljust(w)[0...w]
|
|
32
|
-
end
|
|
39
|
+
end
|
|
40
|
+
"| #{justified_cols.join(' | ')} |"
|
|
33
41
|
end
|
|
34
42
|
|
|
35
43
|
# Add a separator line to the ASCII table
|
|
36
44
|
def separator
|
|
37
|
-
|
|
45
|
+
columns_dashes = @widths.map { |w| '-' * (w + 2) }
|
|
46
|
+
"+#{columns_dashes.join('+')}+"
|
|
38
47
|
end
|
|
39
48
|
end
|
|
40
49
|
|
|
@@ -57,25 +66,78 @@ module PProf
|
|
|
57
66
|
# Decide what to print. Valid keys are :info, :certs and :devices
|
|
58
67
|
#
|
|
59
68
|
def print_info(profile, options = nil)
|
|
60
|
-
options ||= { :
|
|
69
|
+
options ||= { info: true }
|
|
61
70
|
if options[:info]
|
|
62
|
-
keys =
|
|
71
|
+
keys = MAIN_PROFILE_KEYS
|
|
63
72
|
keys.each do |key|
|
|
64
|
-
@output.puts "- #{key
|
|
73
|
+
@output.puts "- #{key}: #{profile.send(key.to_sym)}"
|
|
65
74
|
end
|
|
66
|
-
@output.puts
|
|
67
|
-
@output.puts
|
|
75
|
+
@output.puts '- Entitlements:'
|
|
76
|
+
@output.puts(profile.entitlements.to_s.split("\n").map { |line| " #{line}" })
|
|
68
77
|
end
|
|
69
78
|
|
|
70
|
-
|
|
79
|
+
# rubocop:disable Style/GuardClause
|
|
80
|
+
if options[:info] || options[:certs]
|
|
71
81
|
@output.puts "- #{profile.developer_certificates.count} Developer Certificates"
|
|
72
|
-
|
|
82
|
+
if options[:certs]
|
|
83
|
+
profile.developer_certificates.each do |cert|
|
|
84
|
+
@output.puts " - #{cert.subject}"
|
|
85
|
+
@output.puts " issuer: #{cert.issuer}"
|
|
86
|
+
@output.puts " serial: #{cert.serial}"
|
|
87
|
+
@output.puts " expires: #{cert.not_after}"
|
|
88
|
+
end
|
|
89
|
+
end
|
|
73
90
|
end
|
|
91
|
+
|
|
74
92
|
if options[:info] || options[:devices]
|
|
75
93
|
@output.puts "- #{(profile.provisioned_devices || []).count} Provisioned Devices"
|
|
76
94
|
profile.provisioned_devices.each { |udid| @output.puts " - #{udid}" } if options[:devices]
|
|
77
95
|
@output.puts "- Provision all devices: #{profile.provisions_all_devices.inspect}"
|
|
78
96
|
end
|
|
97
|
+
# rubocop:enable Style/GuardClause
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Returns a Provisioning Profile hash ready to be printed as a JSON output
|
|
101
|
+
#
|
|
102
|
+
# @param [Array<PProf::ProvisioningProfile>] profile
|
|
103
|
+
# List of provisioning profiles to include in the JSON output
|
|
104
|
+
# @param [Hash] options
|
|
105
|
+
# Options to indicate what to include in the generated JSON.
|
|
106
|
+
# `:certs`: if set to `true`, output will also include the info about `DeveloperCertificates` in each profile
|
|
107
|
+
# `:devices`: if set to `true`, output will also include the list of `ProvisionedDevices` for each profile
|
|
108
|
+
#
|
|
109
|
+
# @return [Hash] The hash ready to be `JSON.pretty_generate`'d
|
|
110
|
+
#
|
|
111
|
+
def as_json(profile, options = {})
|
|
112
|
+
hash = profile.to_hash.dup
|
|
113
|
+
hash.delete 'DER-Encoded-Profile'
|
|
114
|
+
hash.delete 'ProvisionedDevices' unless options[:devices]
|
|
115
|
+
if options[:certs]
|
|
116
|
+
hash['DeveloperCertificates'] = developer_certificates.map do |cert|
|
|
117
|
+
{
|
|
118
|
+
subject: cert.subject,
|
|
119
|
+
issuer: cert.issuer,
|
|
120
|
+
serial: cert.serial,
|
|
121
|
+
expires: cert.not_after
|
|
122
|
+
}
|
|
123
|
+
end
|
|
124
|
+
else
|
|
125
|
+
hash.delete 'DeveloperCertificates'
|
|
126
|
+
end
|
|
127
|
+
hash
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Prints a Provisioning Profile as JSON
|
|
131
|
+
#
|
|
132
|
+
# @param [Array<PProf::ProvisioningProfile>] profile
|
|
133
|
+
# List of provisioning profiles to include in the JSON output
|
|
134
|
+
# @param [Hash] options
|
|
135
|
+
# Options to indicate what to include in the generated JSON.
|
|
136
|
+
# `:certs`: if set to `true`, output will also include the info about `DeveloperCertificates` in each profile
|
|
137
|
+
# `:devices`: if set to `true`, output will also include the list of `ProvisionedDevices` for each profile
|
|
138
|
+
#
|
|
139
|
+
def print_json(profile, options = {})
|
|
140
|
+
@output.puts JSON.pretty_generate(as_json(profile, options))
|
|
79
141
|
end
|
|
80
142
|
|
|
81
143
|
# Prints the filtered list of Provisioning Profiles
|
|
@@ -97,23 +159,25 @@ module PProf
|
|
|
97
159
|
# * Valid values for key `:zero` are `true` or `false` to decide if we print `\0` at the end of each output.
|
|
98
160
|
# Only used by `:list` and `:path` modes
|
|
99
161
|
#
|
|
100
|
-
def print_filtered_list(dir = PProf::ProvisioningProfile::DEFAULT_DIR, filters = {}, list_options = { :
|
|
162
|
+
def print_filtered_list(dir = PProf::ProvisioningProfile::DEFAULT_DIR, filters = {}, list_options = { mode: :table })
|
|
101
163
|
filter_func = lambda do |p|
|
|
102
164
|
(filters[:name].nil? || p.name =~ filters[:name]) &&
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
165
|
+
(filters[:appid_name].nil? || p.app_id_name =~ filters[:appid_name]) &&
|
|
166
|
+
(filters[:appid].nil? || p.entitlements.app_id =~ filters[:appid]) &&
|
|
167
|
+
(filters[:uuid].nil? || p.uuid =~ filters[:uuid]) &&
|
|
168
|
+
(filters[:team].nil? || p.team_name =~ filters[:team] || p.team_ids.any? { |id| id =~ filters[:team] }) &&
|
|
169
|
+
(filters[:exp].nil? || (p.expiration_date < DateTime.now) == filters[:exp]) &&
|
|
170
|
+
(filters[:has_devices].nil? || !(p.provisioned_devices || []).empty? == filters[:has_devices]) &&
|
|
171
|
+
(filters[:all_devices].nil? || p.provisions_all_devices == filters[:all_devices]) &&
|
|
172
|
+
(filters[:aps_env].nil? || match_aps_env(p.entitlements.aps_environment, filters[:aps_env])) &&
|
|
173
|
+
true
|
|
112
174
|
end
|
|
113
175
|
|
|
114
176
|
case list_options[:mode]
|
|
115
177
|
when :table
|
|
116
178
|
print_table(dir, &filter_func)
|
|
179
|
+
when :json
|
|
180
|
+
print_json_list(dir, list_options, &filter_func)
|
|
117
181
|
else
|
|
118
182
|
print_list(dir, list_options, &filter_func)
|
|
119
183
|
end
|
|
@@ -138,16 +202,17 @@ module PProf
|
|
|
138
202
|
@output.puts table.row('UUID', 'Name', 'AppID', 'Expiration Date', ' ', 'Team Name')
|
|
139
203
|
@output.puts table.separator
|
|
140
204
|
|
|
141
|
-
Dir[
|
|
205
|
+
Dir['*.mobileprovision', base: dir].each do |file_name|
|
|
206
|
+
file = File.join(dir, file_name)
|
|
142
207
|
begin
|
|
143
208
|
p = PProf::ProvisioningProfile.new(file)
|
|
144
|
-
|
|
209
|
+
|
|
145
210
|
next if block_given? && !yield(p)
|
|
146
211
|
|
|
147
212
|
state = DateTime.now < p.expiration_date ? "\u{2705}" : "\u{274c}" # 2705=checkmark, 274C=red X
|
|
148
213
|
@output.puts table.row(p.uuid, p.name, p.entitlements.app_id, p.expiration_date.to_time, state, p.team_name)
|
|
149
|
-
rescue
|
|
150
|
-
errors << { :
|
|
214
|
+
rescue StandardError => e
|
|
215
|
+
errors << { message: e, file: file }
|
|
151
216
|
end
|
|
152
217
|
count += 1
|
|
153
218
|
end
|
|
@@ -155,9 +220,7 @@ module PProf
|
|
|
155
220
|
@output.puts table.separator
|
|
156
221
|
@output.puts "#{count} Provisioning Profiles found."
|
|
157
222
|
|
|
158
|
-
unless errors.empty?
|
|
159
|
-
errors.each { |e| print_error(e[:message], e[:file]) }
|
|
160
|
-
end
|
|
223
|
+
errors.each { |e| print_error(e[:message], e[:file]) } unless errors.empty?
|
|
161
224
|
end
|
|
162
225
|
|
|
163
226
|
# Prints the filtered list of UUIDs or Paths only
|
|
@@ -165,35 +228,62 @@ module PProf
|
|
|
165
228
|
# @param [String] dir
|
|
166
229
|
# The directory containing the mobileprovision files to list.
|
|
167
230
|
# Defaults to '~/Library/MobileDevice/Provisioning Profiles'
|
|
231
|
+
# @param [Hash] options
|
|
232
|
+
# The options hash typically filled while parsing the command line arguments.
|
|
233
|
+
# - :mode: will print the UUIDs if set to `:list`, the file path otherwise
|
|
234
|
+
# - :zero: will concatenate the entries with `\0` instead of `\n` if set
|
|
168
235
|
#
|
|
169
|
-
# @yield each provisioning
|
|
236
|
+
# @yield each provisioning profile for filtering/validation
|
|
170
237
|
# The block is given ProvisioningProfile object and should
|
|
171
238
|
# return true to display the row, false to filter it out
|
|
172
239
|
#
|
|
173
|
-
def print_list(dir = PProf::ProvisioningProfile::DEFAULT_DIR, options)
|
|
240
|
+
def print_list(dir = PProf::ProvisioningProfile::DEFAULT_DIR, options) # rubocop:disable Style/OptionalArguments
|
|
174
241
|
errors = []
|
|
175
|
-
Dir[
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
242
|
+
Dir['*.mobileprovision', base: dir].each do |file_name|
|
|
243
|
+
file = File.join(dir, file_name)
|
|
244
|
+
p = PProf::ProvisioningProfile.new(file)
|
|
245
|
+
next if block_given? && !yield(p)
|
|
179
246
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
end
|
|
185
|
-
end
|
|
186
|
-
unless errors.empty?
|
|
187
|
-
errors.each { |e| print_error(e[:message], e[:file]) }
|
|
247
|
+
@output.print options[:mode] == :list ? p.uuid.chomp : file.chomp
|
|
248
|
+
@output.print options[:zero] ? "\0" : "\n"
|
|
249
|
+
rescue StandardError => e
|
|
250
|
+
errors << { message: e, file: file }
|
|
188
251
|
end
|
|
252
|
+
errors.each { |e| print_error(e[:message], e[:file]) } unless errors.empty?
|
|
189
253
|
end
|
|
190
254
|
|
|
255
|
+
# Prints the filtered list of profiles as a JSON array
|
|
256
|
+
#
|
|
257
|
+
# @param [String] dir
|
|
258
|
+
# The directory containing the mobileprovision files to list.
|
|
259
|
+
# Defaults to '~/Library/MobileDevice/Provisioning Profiles'
|
|
260
|
+
# @param [Hash] options
|
|
261
|
+
# The options hash typically filled while parsing the command line arguments.
|
|
262
|
+
# - :mode: will print the UUIDs if set to `:list`, the file path otherwise
|
|
263
|
+
# - :zero: will concatenate the entries with `\0` instead of `\n` if set
|
|
264
|
+
#
|
|
265
|
+
# @yield each provisioning profile for filtering/validation
|
|
266
|
+
# The block is given ProvisioningProfile object and should
|
|
267
|
+
# return true to display the row, false to filter it out
|
|
268
|
+
#
|
|
269
|
+
def print_json_list(dir = PProf::ProvisioningProfile::DEFAULT_DIR, options) # rubocop:disable Style/OptionalArguments
|
|
270
|
+
errors = []
|
|
271
|
+
profiles = Dir['*.mobileprovision', base: dir].map do |file_name|
|
|
272
|
+
file = File.join(dir, file_name)
|
|
273
|
+
p = PProf::ProvisioningProfile.new(file)
|
|
274
|
+
as_json(p, options) unless block_given? && !yield(p)
|
|
275
|
+
rescue StandardError => e
|
|
276
|
+
errors << { message: e, file: file }
|
|
277
|
+
end.compact
|
|
278
|
+
errors.each { |e| print_error(e[:message], e[:file]) } unless errors.empty?
|
|
279
|
+
@output.puts JSON.pretty_generate(profiles)
|
|
280
|
+
end
|
|
191
281
|
|
|
192
|
-
private
|
|
193
282
|
def self.match_aps_env(actual, expected)
|
|
194
|
-
return false if actual.nil?
|
|
195
|
-
return true if expected
|
|
196
|
-
|
|
283
|
+
return false if actual.nil? # false if no Push entitlements
|
|
284
|
+
return true if expected == true # true if Push present but we don't filter on specific env
|
|
285
|
+
|
|
286
|
+
actual =~ expected # true if Push present and we filter on specific env
|
|
197
287
|
end
|
|
198
288
|
end
|
|
199
289
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'openssl'
|
|
2
4
|
require 'plist'
|
|
3
5
|
require 'time'
|
|
@@ -7,7 +9,7 @@ module PProf
|
|
|
7
9
|
# Represents the content of a Provisioning Profile file
|
|
8
10
|
class ProvisioningProfile
|
|
9
11
|
# The default location where all the Provisioning Profiles are stored on a Mac
|
|
10
|
-
DEFAULT_DIR="#{ENV['HOME']}/Library/MobileDevice/Provisioning Profiles"
|
|
12
|
+
DEFAULT_DIR = "#{ENV['HOME']}/Library/MobileDevice/Provisioning Profiles"
|
|
11
13
|
|
|
12
14
|
# Create a new ProvisioningProfile object from a file path or UUID
|
|
13
15
|
#
|
|
@@ -19,26 +21,26 @@ module PProf
|
|
|
19
21
|
# File path or UUID of the ProvisioningProfile
|
|
20
22
|
#
|
|
21
23
|
def initialize(file)
|
|
22
|
-
if file =~
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
path = if file =~ /^[0-9A-F-]*$/i
|
|
25
|
+
"#{PProf::ProvisioningProfile::DEFAULT_DIR}/#{file}.mobileprovision"
|
|
26
|
+
else
|
|
27
|
+
file
|
|
28
|
+
end
|
|
27
29
|
xml = nil
|
|
28
30
|
begin
|
|
29
31
|
pkcs7 = OpenSSL::PKCS7.new(File.read(path))
|
|
30
32
|
pkcs7.verify([], OpenSSL::X509::Store.new)
|
|
31
33
|
xml = pkcs7.data
|
|
32
|
-
raise
|
|
33
|
-
rescue
|
|
34
|
+
raise 'Empty PKCS7 payload' if xml.nil? || xml.empty?
|
|
35
|
+
rescue StandardError
|
|
34
36
|
# Seems like sometimes OpenSSL fails to parse the PKCS7 payload
|
|
35
37
|
# Besides, OpenSSL is deprecated on macOS so might not be up-to-date on all machines
|
|
36
38
|
# So as a fallback, we run the `security` command line.
|
|
37
|
-
# (We could use `security` everytime, but invoking a command line is generally less
|
|
39
|
+
# (We could use `security` everytime, but invoking a command line is generally less
|
|
38
40
|
# efficient than calling the OpenSSL gem if available, so for now it's just used as fallback)
|
|
39
41
|
xml = `security cms -D -i "#{path}" 2> /dev/null`
|
|
40
42
|
end
|
|
41
|
-
@plist = Plist
|
|
43
|
+
@plist = Plist.parse_xml(xml)
|
|
42
44
|
raise "Unable to parse file #{file}." if @plist.nil?
|
|
43
45
|
end
|
|
44
46
|
|
|
@@ -101,7 +103,7 @@ module PProf
|
|
|
101
103
|
def team_ids
|
|
102
104
|
@plist['TeamIdentifier']
|
|
103
105
|
end
|
|
104
|
-
|
|
106
|
+
|
|
105
107
|
# The name of the Team associated with this Provisioning Profile
|
|
106
108
|
#
|
|
107
109
|
# @return [String]
|
|
@@ -109,7 +111,7 @@ module PProf
|
|
|
109
111
|
@plist['TeamName']
|
|
110
112
|
end
|
|
111
113
|
|
|
112
|
-
# The list of X509 Developer
|
|
114
|
+
# The list of X509 Developer Certificates associated with this profile
|
|
113
115
|
#
|
|
114
116
|
# @return [Array<OpenSSL::X509::Certificate>]
|
|
115
117
|
def developer_certificates
|
|
@@ -144,7 +146,7 @@ module PProf
|
|
|
144
146
|
#
|
|
145
147
|
# @return [Hash]
|
|
146
148
|
def to_hash
|
|
147
|
-
@
|
|
149
|
+
@plist
|
|
148
150
|
end
|
|
149
151
|
|
|
150
152
|
# The human-readable string representation of this Provisioning Profile
|
|
@@ -152,14 +154,14 @@ module PProf
|
|
|
152
154
|
#
|
|
153
155
|
# @return [String]
|
|
154
156
|
def to_s
|
|
155
|
-
lines = [
|
|
156
|
-
"- #{key
|
|
157
|
+
lines = %i[name uuid app_id_name app_id_prefix creation_date expiration_date ttl team_ids team_name].map do |key|
|
|
158
|
+
"- #{key}: #{send(key.to_sym)}"
|
|
157
159
|
end +
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
160
|
+
[
|
|
161
|
+
"- #{developer_certificates.count} Developer Certificates",
|
|
162
|
+
"- #{provisioned_devices.count} Provisioned Devices",
|
|
163
|
+
'- Entitlements:'
|
|
164
|
+
] + entitlements.to_hash.map { |key, value| " - #{key}: #{value}" }
|
|
163
165
|
lines.join("\n")
|
|
164
166
|
end
|
|
165
167
|
end
|
data/lib/pprof/version.rb
CHANGED
data/lib/pprof.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pprof
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Olivier Halligon
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2022-05-09 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: plist
|
|
@@ -42,8 +42,9 @@ files:
|
|
|
42
42
|
homepage: https://github.com/AliSoftware/pprof
|
|
43
43
|
licenses:
|
|
44
44
|
- MIT
|
|
45
|
-
metadata:
|
|
46
|
-
|
|
45
|
+
metadata:
|
|
46
|
+
rubygems_mfa_required: 'true'
|
|
47
|
+
post_install_message:
|
|
47
48
|
rdoc_options: []
|
|
48
49
|
require_paths:
|
|
49
50
|
- lib
|
|
@@ -51,16 +52,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
51
52
|
requirements:
|
|
52
53
|
- - ">="
|
|
53
54
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: 2.
|
|
55
|
+
version: 2.6.4
|
|
55
56
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
56
57
|
requirements:
|
|
57
58
|
- - ">="
|
|
58
59
|
- !ruby/object:Gem::Version
|
|
59
60
|
version: '0'
|
|
60
61
|
requirements: []
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
signing_key:
|
|
62
|
+
rubygems_version: 3.0.3
|
|
63
|
+
signing_key:
|
|
64
64
|
specification_version: 4
|
|
65
65
|
summary: A Provisioning Profiles library
|
|
66
66
|
test_files: []
|