Class: Spaceship::Portal::ProvisioningProfile

Inherits:
Spaceship::PortalBase show all
Defined in:
spaceship/lib/spaceship/portal/provisioning_profile.rb

Overview

Represents a provisioning profile of the Apple Dev Portal

NOTE: If the environment variable ‘SPACESHIP_AVOID_XCODE_API` is present when using this class, all requests will be made via Apple developer portal API. In the default case, this class will use the Xcode API for fetching provisioning profiles. This is an optimization that results in 1 query for all Profiles vs 1+N queries.

Direct Known Subclasses

AdHoc, AppStore, Development, Direct, InHouse

Defined Under Namespace

Classes: AdHoc, AppStore, Development, Direct, InHouse

Instance Attribute Summary collapse

Attributes inherited from Base

#client, #raw_data

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Spaceship::PortalBase

client

Methods inherited from Base

attr_accessor, attr_mapping, #attributes, attributes, #initialize, #inspect, mapping_module, method_missing, set_client, #setup, #to_s

Constructor Details

This class inherits a constructor from Spaceship::Base

Instance Attribute Details

#appApp

A reference to the app this profile is for. You can then easily access the value directly

Examples:

Example Value

<Spaceship::Portal::App
  @app_id="2UMR2S6PAA"
  @name="App Name"
  @platform="ios"
  @prefix="5A997XSAAA"
  @bundle_id="com.krausefx.app"
  @is_wildcard=false
  @dev_push_enabled=false
  @prod_push_enabled=false>

Usage

profile.app.name


101
102
103
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 101

def app
  @app
end

#certificatesArray

Returns A list of certificates used for this profile.

Examples:

Example Value

[
 <Spaceship::Portal::Certificate::Production
   @status=nil
   @id="XC5PH8D4AA"
   @name="iOS Distribution"
   @created=nil
   @expires=#<DateTime: 2015-11-25T22:45:50+00:00 ((2457352j,81950s,0n),+0s,2299161j)>
   @owner_type="team"
   @owner_name=nil
   @owner_id=nil
   @type_display_id="R58UK2EWAA">]
]

Usage

profile.certificates.first.id


120
121
122
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 120

def certificates
  @certificates
end

#devicesArray

Returns A list of devices this profile is enabled for. This will always be [] for AppStore profiles.

Examples:

Example Value

<Spaceship::Portal::Device
  @id="WXQ7V239BE"
  @name="Grahams iPhone 4s"
  @udid="ba0ac7d70f7a14c6fa02ef0e02f4fe9c5178e2f7"
  @platform="ios"
  @status="c">]

Usage

profile.devices.first.name


135
136
137
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 135

def devices
  @devices
end

#distribution_methodString

Returns The profile distribution type. You probably want to use the class type to detect the profile type instead of this string.

Examples:

AppStore Profile

"store"

AdHoc Profile

"adhoc"

Development Profile

"limited"

Mac Developer ID Profile

"direct"


42
43
44
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 42

def distribution_method
  @distribution_method
end

#expiresDateTime

Returns The date and time of when the profile expires.

Examples:

#<DateTime: 2015-11-25T22:45:50+00:00 ((2457352j,81950s,0n),+0s,2299161j)>


30
31
32
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 30

def expires
  @expires
end

#idString

Returns The ID generated by the Dev Portal You’ll probably not really need this value.

Examples:

"2MAY7NPHAA"


17
18
19
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 17

def id
  @id
end

#is_template_profileBool

Returns Does the profile use a template (has extended entitlements)?.

Examples:

false


146
147
148
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 146

def is_template_profile
  @is_template_profile
end

#managing_appObject

No information about this attribute



82
83
84
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 82

def managing_app
  @managing_app
end

#nameString

Returns The name of this profile.

Examples:

"com.krausefx.app AppStore"


47
48
49
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 47

def name
  @name
end

#platformString

Returns The supported platform for this profile.

Examples:

"ios"


74
75
76
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 74

def platform
  @platform
end

#profile_detailsObject

This is an expensive operation as it triggers a new request



140
141
142
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 140

def profile_details
  @profile_details
end

#statusString

Returns The status of this profile.

Examples:

Active (profile is fine)

"Active"

Expired (time ran out)

"Expired"

Invalid (e.g. code signing identity not available any more)

"Invalid"


56
57
58
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 56

def status
  @status
end

#sub_platformString

Returns The supported sub_platform for this profile.

Examples:

"tvOS"


79
80
81
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 79

def sub_platform
  @sub_platform
end

#templateBool

Lazily instantiates the provisioning profile template model



163
164
165
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 163

def template
  @template
end

#typeString

Returns The type of the profile (development or distribution). You’ll probably not need this value.

Examples:

Distribution

"iOS Distribution"

Development

"iOS Development"


64
65
66
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 64

def type
  @type
end

#uuidString

Returns The UDID of this provisioning profile This value is used for example for code signing It is also contained in the actual profile.

Examples:

"23d7df3b-9767-4e85-a1ea-1df4d8f32fec"


24
25
26
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 24

def uuid
  @uuid
end

#versionString

Returns This will always be “2”.

Examples:

"2"


69
70
71
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 69

def version
  @version
end

Class Method Details

.all(mac: false, xcode: false) ⇒ Array



315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 315

def all(mac: false, xcode: false)
  if ENV['SPACESHIP_AVOID_XCODE_API']
    profiles = client.provisioning_profiles(mac: mac)
  else
    profiles = client.provisioning_profiles_via_xcode_api(mac: mac)
  end

  # transform raw data to class instances
  profiles.map! { |profile| self.factory(profile) }

  # filter out the profiles managed by xcode
  unless xcode
    profiles.delete_if(&:managed_by_xcode?)
  end

  return profiles if self == ProvisioningProfile
  return profiles.select { |profile| profile.class == self }
end

.all_tvosArray



337
338
339
340
341
342
343
344
345
346
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 337

def all_tvos
  profiles = all(mac: false)
  tv_os_profiles = []
  profiles.each do |tv_os_profile|
    if tv_os_profile.tvos?
      tv_os_profiles << tv_os_profile
    end
  end
  return tv_os_profiles
end

.create!(name: nil, bundle_id: nil, certificate: nil, devices: [], mac: false, sub_platform: nil, template_name: nil) ⇒ ProvisioningProfile

Create a new provisioning profile



260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 260

def create!(name: nil, bundle_id: nil, certificate: nil, devices: [], mac: false, sub_platform: nil, template_name: nil)
  raise "Missing required parameter 'bundle_id'" if bundle_id.to_s.empty?
  raise "Missing required parameter 'certificate'. e.g. use `Spaceship::Portal::Certificate::Production.all.first`" if certificate.to_s.empty?

  app = Spaceship::Portal::App.find(bundle_id, mac: mac)
  raise "Could not find app with bundle id '#{bundle_id}'" unless app

  raise "Invalid sub_platform #{sub_platform}, valid values are tvOS" if !sub_platform.nil? && sub_platform != 'tvOS'

  # Fill in sensible default values
  name ||= [bundle_id, self.pretty_type].join(' ')

  if self == AppStore || self == InHouse || self == Direct
    # Distribution Profiles MUST NOT have devices
    devices = []
  end

  certificate_parameter = certificate.collect(&:id) if certificate.kind_of?(Array)
  certificate_parameter ||= [certificate.id]

  # Fix https://github.com/KrauseFx/fastlane/issues/349
  certificate_parameter = certificate_parameter.first if certificate_parameter.count == 1

  if devices.nil? || devices.count == 0
    if self == Development || self == AdHoc
      # For Development and AdHoc we usually want all compatible devices by default
      if mac
        devices = Spaceship::Portal::Device.all_macs
      elsif sub_platform == 'tvOS'
        devices = Spaceship::Portal::Device.all_apple_tvs
      else
        devices = Spaceship::Portal::Device.all_ios_profile_devices
      end
    end
  end

  profile = client.with_retry do
    client.create_provisioning_profile!(name,
                                        self.type,
                                        app.app_id,
                                        certificate_parameter,
                                        devices.map(&:id),
                                        mac: mac,
                                        sub_platform: sub_platform,
                                        template_name: template_name)
  end

  self.new(profile)
end

.factory(attrs) ⇒ Object

Create a new object based on a hash. This is used to create a new object based on the server response.



195
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
225
226
227
228
229
230
231
232
233
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 195

def factory(attrs)
  # available values of `distributionMethod` at this point: ['adhoc', 'store', 'limited', 'direct', 'inhouse']
  klass = case attrs['distributionMethod']
          when 'limited'
            Development
          when 'store'
            AppStore
          when 'adhoc'
            AdHoc
          when 'inhouse'
            InHouse
          when 'direct'
            Direct # Mac-only
          else
            raise "Can't find class '#{attrs['distributionMethod']}'"
          end

  # Parse the dates
  # rubocop:disable Style/RescueModifier
  attrs['dateExpire'] = (Time.parse(attrs['dateExpire']) rescue attrs['dateExpire'])
  # rubocop:enable Style/RescueModifier

  # When a profile is created with a template name, the response
  # (provisioning profiles info) already contains the data about
  # template, which is used to instantiate the
  # ProvisioningProfileTemplate model.
  # Doing so saves an API call needed to fetch profile details.
  #
  # Verify if `attrs` contains the info needed to instantiate a template.
  # If not, the template will be lazily loaded.
  if attrs['profile'] && attrs['profile']['description']
    attrs['template'] = ProvisioningProfileTemplate.factory(attrs['template'])
  end

  klass.client = @client
  obj = klass.new(attrs)

  return obj
end

.find_by_bundle_id(bundle_id: nil, mac: false, sub_platform: nil) ⇒ Array



352
353
354
355
356
357
358
359
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 352

def find_by_bundle_id(bundle_id: nil, mac: false, sub_platform: nil)
  raise "Missing required parameter 'bundle_id'" if bundle_id.to_s.empty?
  raise "Invalid sub_platform #{sub_platform}, valid values are tvOS" if !sub_platform.nil? && sub_platform != 'tvOS'
  find_tvos_profiles = sub_platform == 'tvOS'
  all(mac: mac).find_all do |profile|
    profile.app.bundle_id == bundle_id && profile.tvos? == find_tvos_profiles
  end
end

.pretty_typeString

Returns The human readable name of this profile type.

Examples:

"AppStore"
"AdHoc"
"Development"
"InHouse"


241
242
243
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 241

def pretty_type
  name.split('::').last
end

.typeString

Returns The profile type used for web requests to the Dev Portal.

Examples:

"limited"
"store"
"adhoc"
"inhouse"


189
190
191
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 189

def type
  raise "You cannot create a ProvisioningProfile without a type. Use a subclass."
end

Instance Method Details

#certificate_valid?Bool

Is the certificate of this profile available?



474
475
476
477
478
479
480
481
482
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 474

def certificate_valid?
  return false if (certificates || []).count == 0
  certificates.each do |c|
    if Spaceship::Portal::Certificate.all(mac: mac?).collect(&:id).include?(c.id)
      return true
    end
  end
  return false
end

#delete!Object

Delete the provisioning profile



409
410
411
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 409

def delete!
  client.delete_provisioning_profile!(self.id, mac: mac?)
end

#downloadString

Download the current provisioning profile. This will not store the provisioning profile on the file system. Instead this method will return the content of the profile.

Examples:

File.write("path.mobileprovision", profile.download)


404
405
406
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 404

def download
  client.download_provisioning_profile(self.id, mac: mac?)
end

#mac?Bool



496
497
498
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 496

def mac?
  platform == 'mac'
end

#managed_by_xcode?Bool



491
492
493
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 491

def managed_by_xcode?
  managing_app == 'Xcode'
end

#repair!ProvisioningProfile

Repair an existing provisioning profile alias to update!



417
418
419
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 417

def repair!
  update!
end

#template_nameString



554
555
556
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 554

def template_name
  is_template_profile ? template.purpose_display_name : nil
end

#tvos?Bool



501
502
503
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 501

def tvos?
  sub_platform == 'tvOS'
end

#update!ProvisioningProfile

Updates the provisioning profile from the local data e.g. after you added new devices to the profile This will also update the code signing identity if necessary



426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 426

def update!
  # sigh handles more specific filtering and validation steps that make this logic OK
  #
  # This is the minimum protection needed for people using spaceship directly
  unless certificate_valid?
    if mac?
      if self.kind_of?(Development)
        self.certificates = [Spaceship::Portal::Certificate::MacDevelopment.all.first]
      elsif self.kind_of?(Direct)
        self.certificates = [Spaceship::Portal::Certificate::DeveloperIdApplication.all.first]
      else
        self.certificates = [Spaceship::Portal::Certificate::MacAppDistribution.all.first]
      end
    else
      if self.kind_of?(Development)
        self.certificates = [Spaceship::Portal::Certificate::Development.all.first]
      elsif self.kind_of?(InHouse)
        self.certificates = [Spaceship::Portal::Certificate::InHouse.all.first]
      else
        self.certificates = [Spaceship::Portal::Certificate::Production.all.first]
      end
    end
  end

  client.with_retry do
    client.repair_provisioning_profile!(
      id,
      name,
      distribution_method,
      app.app_id,
      certificates.map(&:id),
      devices.map(&:id),
      mac: mac?,
      sub_platform: tvos? ? 'tvOS' : nil,
      template_name: is_template_profile ? template.purpose_name : nil
    )
  end

  # We need to fetch the provisioning profile again, as the ID changes
  profile = Spaceship::Portal::ProvisioningProfile.all(mac: mac?).find do |p|
    p.name == self.name # we can use the name as it's valid
  end

  return profile
end

#valid?Bool



486
487
488
# File 'spaceship/lib/spaceship/portal/provisioning_profile.rb', line 486

def valid?
  return status == 'Active'
end