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[name uuid app_id_name app_id_prefix creation_date expiration_date ttl team_ids team_name]

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



282
283
284
285
286
287
# File 'lib/pprof/output_formatter.rb', line 282

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
# File 'lib/pprof/output_formatter.rb', line 111

def as_json(profile, options = {})
  hash = profile.to_hash.dup
  hash.delete 'DER-Encoded-Profile'
  hash.delete 'ProvisionedDevices' unless options[:devices]
  if options[:certs]
    hash['DeveloperCertificates'] = 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

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 filtered list of Provisioning Profiles

Convenience method. Calls self.print_list with a filter block build from a filter hash

Parameters:

  • dir (String) (defaults to: PProf::ProvisioningProfile::DEFAULT_DIR)

    The directory to search for the provisioning profiles. Defaults to the standard directory on Mac

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

    The hash describing the applied filters

  • list_options (Hash<Symbol,Any>) (defaults to: { mode: :table })

    The way to print the output.

    • Valid values for key ‘:mode` are:

      • ‘:table` (for ASCII table output)

      • ‘:list` (for plain list of only the UUIDs, suitable for piping to `xargs`)

      • ‘:path` (for plain list of only the paths, suitable for piping to `xargs`)

    • Valid values for key ‘:zero` are `true` or `false` to decide if we print `0` at the end of each output. Only used by `:list` and `:path` modes



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/pprof/output_formatter.rb', line 162

def print_filtered_list(dir = PProf::ProvisioningProfile::DEFAULT_DIR, filters = {}, list_options = { mode: :table })
  filter_func = 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])) &&
      true
  end

  case list_options[:mode]
  when :table
    print_table(dir, &filter_func)
  when :json
    print_json_list(dir, list_options, &filter_func)
  else
    print_list(dir, list_options, &filter_func)
  end
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



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

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:

  • dir (String) (defaults to: PProf::ProvisioningProfile::DEFAULT_DIR)

    The directory containing the mobileprovision files to list. Defaults to ‘~/Library/MobileDevice/Provisioning Profiles’

  • 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
    

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



269
270
271
272
273
274
275
276
277
278
279
280
# File 'lib/pprof/output_formatter.rb', line 269

def print_json_list(dir = PProf::ProvisioningProfile::DEFAULT_DIR, options) # rubocop:disable Style/OptionalArguments
  errors = []
  profiles = Dir['*.mobileprovision', 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.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:

  • dir (String) (defaults to: PProf::ProvisioningProfile::DEFAULT_DIR)

    The directory containing the mobileprovision files to list. Defaults to ‘~/Library/MobileDevice/Provisioning Profiles’

  • 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
    

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



240
241
242
243
244
245
246
247
248
249
250
251
252
253
# File 'lib/pprof/output_formatter.rb', line 240

def print_list(dir = PProf::ProvisioningProfile::DEFAULT_DIR, options) # rubocop:disable Style/OptionalArguments
  errors = []
  Dir['*.mobileprovision', 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
  errors.each { |e| print_error(e[:message], e[:file]) } unless errors.empty?
end

Prints the filtered list as a table

Parameters:

  • dir (String) (defaults to: PProf::ProvisioningProfile::DEFAULT_DIR)

    The directory containing the mobileprovision files to list. Defaults to ‘~/Library/MobileDevice/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



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/pprof/output_formatter.rb', line 196

def print_table(dir = PProf::ProvisioningProfile::DEFAULT_DIR)
  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

  Dir['*.mobileprovision', 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

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

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