Class: PProf::OutputFormatter

Inherits:
Object
  • Object
show all
Defined in:
lib/pprof/output_formatter.rb

Overview

A helper tool to pretty-print Provisioning Profile informations

Defined Under Namespace

Classes: ASCIITable

Constant Summary collapse

MAIN_PROFILE_KEYS =

List of properties of a ‘PProf::ProvisioningProfile` to print when using the `-i` flag

%i[path name uuid app_id_name app_id_prefix creation_date expiration_date ttl team_ids team_name].freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(output = $stdout) ⇒ OutputFormatter

Initialize a new OutputFormatter

Parameters:

  • output (IO) (defaults to: $stdout)

    The output destination where to print the formatted data. Defaults to $stdout



18
19
20
# File 'lib/pprof/output_formatter.rb', line 18

def initialize(output = $stdout)
  @output = output
end

Class Method Details

.match_aps_env(actual, expected) ⇒ Object



268
269
270
271
272
273
# File 'lib/pprof/output_formatter.rb', line 268

def self.match_aps_env(actual, expected)
  return false if actual.nil? # false if no Push entitlements
  return true if expected == true # true if Push present but we don't filter on specific env

  actual =~ expected # true if Push present and we filter on specific env
end

Instance Method Details

#as_json(profile, options = {}) ⇒ Hash

Returns a Provisioning Profile hash ready to be printed as a JSON output

Parameters:

  • profile (Array<PProf::ProvisioningProfile>)

    List of provisioning profiles to include in the JSON output

  • options (Hash) (defaults to: {})

    Options to indicate what to include in the generated JSON. ‘:certs`: if set to `true`, output will also include the info about `DeveloperCertificates` in each profile `:devices`: if set to `true`, output will also include the list of `ProvisionedDevices` for each profile

Returns:

  • (Hash)

    The hash ready to be ‘JSON.pretty_generate`’d



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/pprof/output_formatter.rb', line 111

def as_json(profile, options = {})
  hash = profile.to_hash.dup
  hash['path'] = profile.path
  hash.delete 'DER-Encoded-Profile'
  hash.delete 'ProvisionedDevices' unless options[:devices]
  if options[:certs]
    hash['DeveloperCertificates'] = profile.developer_certificates.map do |cert|
      {
        subject: cert.subject,
        issuer: cert.issuer,
        serial: cert.serial,
        expires: cert.not_after
      }
    end
  else
    hash.delete 'DeveloperCertificates'
  end
  hash
end

#filter_proc(filters = {}) ⇒ Lambda

Generates a lambda which takes a ‘PProf::ProvisioningProfile` and returns if it should be kept in our listing or not

Parameters:

  • filters (Hash<Symbol,Any>) (defaults to: {})

    The hash describing the applied filters

Returns:

  • (Lambda)

    A lambda which takes a ‘PProf::ProvisioningProfile` and returns `true` if it matches the provided `filters`



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/pprof/output_formatter.rb', line 150

def filter_proc(filters = {})
  lambda do |p|
    (filters[:name].nil? || p.name =~ filters[:name]) &&
      (filters[:appid_name].nil? || p.app_id_name =~ filters[:appid_name]) &&
      (filters[:appid].nil? || p.entitlements.app_id =~ filters[:appid]) &&
      (filters[:uuid].nil? || p.uuid =~ filters[:uuid]) &&
      (filters[:team].nil? || p.team_name =~ filters[:team] || p.team_ids.any? { |id| id =~ filters[:team] }) &&
      (filters[:exp].nil? || (p.expiration_date < DateTime.now) == filters[:exp]) &&
      (filters[:has_devices].nil? || !(p.provisioned_devices || []).empty? == filters[:has_devices]) &&
      (filters[:all_devices].nil? || p.provisions_all_devices == filters[:all_devices]) &&
      (filters[:aps_env].nil? || match_aps_env(p.entitlements.aps_environment, filters[:aps_env])) &&
      (filters[:platform].nil? || p.platform.include?(filters[:platform])) &&
      true
  end
end

Prints an error message

Parameters:

  • message (String)

    The error message to print

  • file (String)

    The provisioning profile file for which the error occurred



57
58
59
# File 'lib/pprof/output_formatter.rb', line 57

def print_error(message, file)
  @output.puts "\u{274c}  #{file} - #{message}"
end

Prints the description of a Provisioning Profile

Parameters:

  • profile (PProf::ProvisioningProfile)

    The ProvisioningProfile object to print

  • options (Hash<Symbol,Bool>) (defaults to: nil)

    Decide what to print. Valid keys are :info, :certs and :devices



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/pprof/output_formatter.rb', line 68

def print_info(profile, options = nil)
  options ||= { info: true }
  if options[:info]
    keys = MAIN_PROFILE_KEYS
    keys.each do |key|
      @output.puts "- #{key}: #{profile.send(key.to_sym)}"
    end
    @output.puts '- Entitlements:'
    @output.puts(profile.entitlements.to_s.split("\n").map { |line| "   #{line}" })
  end

  # rubocop:disable Style/GuardClause
  if options[:info] || options[:certs]
    @output.puts "- #{profile.developer_certificates.count} Developer Certificates"
    if options[:certs]
      profile.developer_certificates.each do |cert|
        @output.puts "   - #{cert.subject}"
        @output.puts "     issuer: #{cert.issuer}"
        @output.puts "     serial: #{cert.serial}"
        @output.puts "     expires: #{cert.not_after}"
      end
    end
  end

  if options[:info] || options[:devices]
    @output.puts "- #{(profile.provisioned_devices || []).count} Provisioned Devices"
    profile.provisioned_devices.each { |udid| @output.puts "   - #{udid}" } if options[:devices]
    @output.puts "- Provision all devices: #{profile.provisions_all_devices.inspect}"
  end
  # rubocop:enable Style/GuardClause
end

Prints a Provisioning Profile as JSON

Parameters:

  • profile (Array<PProf::ProvisioningProfile>)

    List of provisioning profiles to include in the JSON output

  • options (Hash) (defaults to: {})

    Options to indicate what to include in the generated JSON. ‘:certs`: if set to `true`, output will also include the info about `DeveloperCertificates` in each profile `:devices`: if set to `true`, output will also include the list of `ProvisionedDevices` for each profile



140
141
142
# File 'lib/pprof/output_formatter.rb', line 140

def print_json(profile, options = {})
  @output.puts JSON.pretty_generate(as_json(profile, options))
end

Prints the filtered list of profiles as a JSON array

Parameters:

  • options (Hash)

    The options hash typically filled while parsing the command line arguments.

    - :certs: will print the UUIDs if set to `:list`, the file path otherwise
    - :devices: will concatenate the entries with `\0` instead of `\n` if set
    
  • dirs (String) (defaults to: PProf::ProvisioningProfile::DEFAULT_DIRS)

    The directories containing the mobileprovision/provisionprofile files to list. Defaults to [‘~/Library/MobileDevice/Provisioning Profiles’, ‘~/Library/Developer/Xcode/UserData/Provisioning Profiles’]

Yields:

  • each provisioning profile for filtering/validation The block is given ProvisioningProfile object and should return true to display the row, false to filter it out



253
254
255
256
257
258
259
260
261
262
263
264
265
266
# File 'lib/pprof/output_formatter.rb', line 253

def print_json_list(options:, dirs: PProf::ProvisioningProfile::DEFAULT_DIRS)
  errors = []
  profiles = dirs.flat_map do |dir|
    Dir['*.{mobileprovision,provisionprofile}', base: dir].map do |file_name|
      file = File.join(dir, file_name)
      p = PProf::ProvisioningProfile.new(file)
      as_json(p, options) unless block_given? && !yield(p)
    rescue StandardError => e
      errors << { message: e, file: file }
    end
  end.compact
  errors.each { |e| print_error(e[:message], e[:file]) } unless errors.empty?
  @output.puts JSON.pretty_generate(profiles)
end

Prints the filtered list of UUIDs or Paths only

Parameters:

  • options (Hash)

    The options hash typically filled while parsing the command line arguments.

    - :mode: will print the UUIDs if set to `:list`, the file path otherwise
    - :zero: will concatenate the entries with `\0` instead of `\n` if set
    
  • dirs (String) (defaults to: PProf::ProvisioningProfile::DEFAULT_DIRS)

    The directories containing the mobileprovision/provisionprofile files to list. Defaults to [‘~/Library/MobileDevice/Provisioning Profiles’, ‘~/Library/Developer/Xcode/UserData/Provisioning Profiles’]

Yields:

  • each provisioning profile for filtering/validation The block is given ProvisioningProfile object and should return true to display the row, false to filter it out



222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/pprof/output_formatter.rb', line 222

def print_list(options:, dirs: PProf::ProvisioningProfile::DEFAULT_DIRS)
  errors = []
  dirs.each do |dir|
    Dir['*.{mobileprovision,provisionprofile}', base: dir].each do |file_name|
      file = File.join(dir, file_name)
      p = PProf::ProvisioningProfile.new(file)
      next if block_given? && !yield(p)

      @output.print options[:mode] == :list ? p.uuid.chomp : file.chomp
      @output.print options[:zero] ? "\0" : "\n"
    rescue StandardError => e
      errors << { message: e, file: file }
    end
  end
  errors.each { |e| print_error(e[:message], e[:file]) } unless errors.empty?
end

Prints the filtered list as a table

Parameters:

  • dirs (String) (defaults to: PProf::ProvisioningProfile::DEFAULT_DIRS)

    The directories containing the mobileprovision/provisionprofile files to list. Defaults to [‘~/Library/MobileDevice/Provisioning Profiles’, ‘~/Library/Developer/Xcode/UserData/Provisioning Profiles’]

Yields:

  • each provisioning provile for filtering/validation The block is given ProvisioningProfile object and should return true to display the row, false to filter it out



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/pprof/output_formatter.rb', line 176

def print_table(dirs: PProf::ProvisioningProfile::DEFAULT_DIRS)
  count = 0
  errors = []

  table = PProf::OutputFormatter::ASCIITable.new(36, 60, 45, 25, 2, 10)
  @output.puts table.separator
  @output.puts table.row('UUID', 'Name', 'AppID', 'Expiration Date', ' ', 'Team Name')
  @output.puts table.separator

  dirs.each do |dir|
    Dir['*.{mobileprovision,provisionprofile}', base: dir].each do |file_name|
      file = File.join(dir, file_name)
      begin
        p = PProf::ProvisioningProfile.new(file)

        next if block_given? && !yield(p)

        state = DateTime.now < p.expiration_date ? "\u{2705}" : "\u{274c}" # 2705=checkmark, 274C=red X
        @output.puts table.row(p.uuid, p.name, p.entitlements.app_id, p.expiration_date.to_time, state, p.team_name)
      rescue StandardError => e
        errors << { message: e, file: file }
      end
      count += 1
    end
  end

  @output.puts table.separator
  @output.puts "#{count} Provisioning Profiles found."

  errors.each { |e| print_error(e[:message], e[:file]) } unless errors.empty?
end