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].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



261
262
263
264
265
266
# File 'lib/pprof/output_formatter.rb', line 261

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'] = 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



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

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



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:

  • 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
    
  • dir (String) (defaults to: PProf::ProvisioningProfile::DEFAULT_DIR)

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



248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/pprof/output_formatter.rb', line 248

def print_json_list(options:, dir: PProf::ProvisioningProfile::DEFAULT_DIR)
  errors = []
  profiles = 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.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
    
  • dir (String) (defaults to: PProf::ProvisioningProfile::DEFAULT_DIR)

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



219
220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/pprof/output_formatter.rb', line 219

def print_list(options:, dir: PProf::ProvisioningProfile::DEFAULT_DIR)
  errors = []
  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
  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/provisionprofile 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



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

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,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

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

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