Module: Adsedare

Extended by:
Logging
Defined in:
lib/adsedare.rb,
lib/adsedare/version.rb,
lib/adsedare/keychain.rb,
lib/adsedare/xcodeproj.rb,
lib/adsedare/capabilities.rb,
lib/adsedare/export_options.rb,
lib/adsedare/install_profiles.rb

Defined Under Namespace

Classes: AppGroupsCapability, Capability, Error, SimpleCapability

Constant Summary collapse

VERSION =
"0.0.10"
APPLE_CERTS =
[
  "AppleWWDRCAG2.cer",
  "AppleWWDRCAG3.cer",
  "AppleWWDRCAG4.cer",
  "AppleWWDRCAG5.cer",
  "AppleWWDRCAG6.cer",
  "AppleWWDRCAG7.cer",
  "AppleWWDRCAG8.cer",
  "DeveloperIDG2CA.cer",
]
APPLE_CERTS_URL =
"https://www.apple.com/certificateauthority"
APPLE_WWDRCA =
"https://developer.apple.com/certificationauthority/AppleWWDRCA.cer"
ENTITLEMENTS_MAPPING =
{
  "com.apple.security.application-groups" => "APP_GROUPS",
  "com.apple.developer.in-app-payments" => "APPLE_PAY",
  "com.apple.developer.associated-domains" => "ASSOCIATED_DOMAINS",
  "com.apple.developer.healthkit" => "HEALTHKIT",
  "com.apple.developer.homekit" => "HOMEKIT",
  "com.apple.developer.networking.HotspotConfiguration" => "HOTSPOT",
  "com.apple.developer.networking.multipath" => "MULTIPATH",
  "com.apple.developer.networking.networkextension" => "NETWORK_EXTENSION",
  "com.apple.developer.nfc.readersession.formats" => "NFC_TAG_READING",
  "com.apple.developer.networking.vpn.api" => "PERSONAL_VPN",
  "com.apple.external-accessory.wireless-configuration" => "WIRELESS_ACCESSORY_CONFIGURATION",
  "com.apple.developer.siri" => "SIRI",
  "com.apple.developer.pass-type-identifiers" => "WALLET",
  "com.apple.developer.icloud-services" => "ICLOUD",
  "com.apple.developer.icloud-container-identifiers" => "ICLOUD",
  "com.apple.developer.ubiquity-container-identifiers" => "ICLOUD",
  "com.apple.developer.ubiquity-kvstore-identifier" => "ICLOUD",
  "com.apple.developer.ClassKit-environment" => "CLASSKIT",
  "com.apple.developer.authentication-services.autofill-credential-provider" => "AUTOFILL_CREDENTIAL_PROVIDER",
  "com.apple.developer.applesignin" => "SIGN_IN_WITH_APPLE",
  "com.apple.developer.usernotifications.communication" => "COMMUNICATION_NOTIFICATIONS",
  "com.apple.developer.usernotifications.time-sensitive" => "USERNOTIFICATIONS_TIMESENSITIVE",
  "com.apple.developer.group-session" => "GROUP_ACTIVITIES",
  "com.apple.developer.family-controls" => "FAMILY_CONTROLS",
  "com.apple.developer.devicecheck.appattest-environment" => "APP_ATTEST",
  "com.apple.developer.game-center" => "GAME_CENTER",
  "com.apple.developer.carplay-maps" => "CARPLAY_NAVIGATION",
}

Class Method Summary collapse

Methods included from Logging

configure_logger_for, logger, logger_for

Class Method Details

.create_keychain(keychain_path = nil, keychain_password = nil, make_default = true) ⇒ void

This method returns an undefined value.

Create a build keychain with all required intermediate certificates Set following environment variables to add project specific certificates:

  • AD_HOC_CERTIFICATE Path to the ad-hoc certificate

  • AD_HOC_PRIVATE_KEY Path to the ad-hoc private key

  • AD_HOC_KEY_PASSWORD Password for the ad-hoc private key

Parameters:

  • keychain_path (String) (defaults to: nil)

    The path to the keychain

  • keychain_password (String) (defaults to: nil)

    The password for the keychain

  • make_default (Boolean) (defaults to: true)

    Whether to make the keychain the default



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/adsedare/keychain.rb', line 35

def create_keychain(keychain_path = nil, keychain_password = nil, make_default = true)
  raise "Keychain path is not set" unless keychain_path
  raise "Keychain password is not set" unless keychain_password

  keychain_path = File.expand_path(keychain_path)

  logger.info "Creating keychain at '#{keychain_path}'"

  FileUtils.mkdir_p(File.dirname(keychain_path))
  status = system("security create-keychain -p #{keychain_password} #{keychain_path}")
  unless status
    logger.error "Failed to create keychain at '#{keychain_path}'"
    return
  end

  status = system("security list-keychains -d user -s #{keychain_path}")
  unless status
    logger.error "Failed to add keychain to search list"
    return
  end

  APPLE_CERTS.each do |cert|
    logger.info "Downloading certificate '#{cert}'"

    response = Faraday.get(
      "#{APPLE_CERTS_URL}/#{cert}"
    )
    unless response.status == 200
      logger.error "Failed to download certificate '#{cert}'"
      next
    end

    file = Tempfile.new(cert)
    file.write(response.body)
    file.close

    install_certificate(file.path, keychain_path)

    file.unlink
  end

  logger.info "Downloading certificate 'AppleWWDRCA.cer'"
  response = Faraday.get(
    APPLE_WWDRCA
  )
  unless response.status == 200
    logger.error "Failed to download certificate 'AppleWWDRCA.cer'"
  else
    file = Tempfile.new("AppleWWDRCA.cer")
    file.write(response.body)
    file.close

    install_certificate(file.path, keychain_path)

    file.unlink
  end

  ad_hoc_certificate = ENV["AD_HOC_CERTIFICATE"]
  ad_hoc_private_key = ENV["AD_HOC_PRIVATE_KEY"]
  ad_hoc_key_password = ENV["AD_HOC_KEY_PASSWORD"]

  unless ad_hoc_certificate || ad_hoc_private_key || ad_hoc_key_password
    logger.warn "AD_HOC_CERTIFICATE, AD_HOC_PRIVATE_KEY, or AD_HOC_KEY_PASSWORD is not set"
    return
  end

  install_certificate(ad_hoc_certificate, keychain_path, "", "cert")
  install_certificate(ad_hoc_private_key, keychain_path, ad_hoc_key_password, "priv")

  if make_default
    status = system("security default-keychain -d user -s #{keychain_path}")
    unless status
      logger.warn "Failed to set default keychain"
      return
    end
  end

  status = system("security set-keychain-settings #{keychain_path}")
  unless status
    logger.error "Failed to set keychain settings"
    return
  end

  status = system("security set-key-partition-list -S apple-tool:,apple: -k #{keychain_password} #{keychain_path}")
  unless status
    logger.error "Failed to set keychain partition list"
    return
  end

  status = system("security unlock-keychain -p #{keychain_password} #{keychain_path}")
  unless status
    logger.error "Failed to unlock keychain"
    return
  end

  logger.info "Keychain created at '#{keychain_path}'"

  status = system("security find-identity -p codesigning")
  unless status
    logger.error "Failed to find codesigning identity"
    return
  end
end

.get_bundle_map(team_id) ⇒ Object



123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/adsedare.rb', line 123

def get_bundle_map(team_id)
  logger.info "Fetching bundle IDs for team ID '#{team_id}'"

  registered_bundles = Starship::Client.get_bundle_ids(team_id)
  bundle_ids = {}

  registered_bundles.each do |bundle|
    bundle_ids[bundle["attributes"]["identifier"]] = bundle["id"]
  end

  return bundle_ids
end

.get_devices(team_id) ⇒ Object



103
104
105
# File 'lib/adsedare.rb', line 103

def get_devices(team_id)
  Starship::Client.get_devices(team_id)
end

.get_profiles_map(team_id) ⇒ Object



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/adsedare.rb', line 107

def get_profiles_map(team_id)
  logger.info "Fetching profiles for team ID '#{team_id}'"

  registered_profiles = Starship::Client.get_profiles(team_id)
  profiles = {}

  registered_profiles.each do |profile|
    provisioning_profile = Starship::Client::get_provisioning_profile(profile["id"], team_id)
    app_id = provisioning_profile["provisioningProfile"]["appIdId"]

    profiles[app_id] = provisioning_profile
  end

  return profiles
end

.install_profiles(project_path = nil) ⇒ void

This method returns an undefined value.

Install provisioning profiles for a project Expects environment variables to be set:

  • APPSTORE_CONNECT_KEY_ID Key ID from Apple Developer Portal

  • APPSTORE_CONNECT_ISSUER_ID Issuer ID from Apple Developer Portal

  • APPSTORE_CONNECT_KEY P8 key content from Apple Developer Portal

Parameters:

  • project_path (String) (defaults to: nil)

    The path to the Xcode project



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/adsedare/install_profiles.rb', line 18

def install_profiles(project_path = nil)
  raise "Project path is not set" unless project_path

  project = Xcodeproj::Project.open(project_path)

  project_bundles = project.targets.map do |target|
    target.build_configurations.map do |config|
      config.build_settings["PRODUCT_BUNDLE_IDENTIFIER"]
    end
  end.flatten.uniq

  bundles_with_profiles = AppStoreConnect::Client.get_bundles_with_profiles(project_bundles)
  bundle_by_identifier = {}
  profiles_by_id = {}

  bundles_with_profiles["data"].each do |bundle_id|
    bundle_by_identifier[bundle_id["attributes"]["identifier"]] = bundle_id
  end

  bundles_with_profiles["included"].each do |profile|
    profiles_by_id[profile["id"]] = profile
  end

  project_bundles.each do |bundle_identifier|
    bundle_id = bundle_by_identifier[bundle_identifier]
    unless bundle_id
      logger.warn "Bundle '#{bundle_identifier}' is missing in App Store Connect. Skipping."
      next
    end

    logger.info "Bundle '#{bundle_identifier}' resolved to Bundle ID '#{bundle_id["id"]}'"

    profiles = bundle_id["relationships"]["profiles"]["data"]
    unless profiles
      logger.warn "Profile for Bundle ID '#{bundle_id["id"]}' is missing in App Store Connect. Skipping."
      next
    end

    ad_hoc_profile = nil
    profiles.each do |profile|
      profile_id = profile["id"]
      profile = profiles_by_id[profile_id]

      profile_name = profile["attributes"]["name"]
      # Xcode managed profiles can't be used for signing
      if profile_name.start_with?("iOS Team Ad Hoc Provisioning Profile") || profile_name.start_with?("XC")
        next
      end

      if profile["attributes"]["profileType"] == "IOS_APP_ADHOC" && profile["attributes"]["profileState"] == "ACTIVE"
        ad_hoc_profile = profile
        break
      end
    end

    unless ad_hoc_profile
      logger.warn "Profile for Bundle ID '#{bundle_id["id"]}' is missing in App Store Connect. Skipping."
      next
    end

    logger.info "Profile for Bundle ID '#{bundle_id["id"]}' resolved to Profile '#{ad_hoc_profile["attributes"]["name"]}'"

    uuid = ad_hoc_profile["attributes"]["uuid"]
    profile_content = Base64.decode64(ad_hoc_profile["attributes"]["profileContent"])
    profile_path = "#{Dir.home}/Library/MobileDevice/Provisioning Profiles/#{uuid}.mobileprovision"

    FileUtils.mkdir_p(File.dirname(profile_path))
    File.write(profile_path, profile_content)

    logger.info "Profile '#{ad_hoc_profile["attributes"]["name"]}' installed to '#{profile_path}'"
  end
end

.make_export_options(project_path = nil, export_path = nil, team_id = nil, options = {}) ⇒ void

This method returns an undefined value.

Create export options for a project Expects environment variables to be set:

  • APPSTORE_CONNECT_KEY_ID Key ID from Apple Developer Portal

  • APPSTORE_CONNECT_ISSUER_ID Issuer ID from Apple Developer Portal

  • APPSTORE_CONNECT_KEY P8 key content from Apple Developer Portal

Parameters:

  • project_path (String) (defaults to: nil)

    The path to the Xcode project

  • export_path (String) (defaults to: nil)

    The path to the export options plist

  • team_id (String) (defaults to: nil)

    The team ID (optional)

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

    Additional options (optional)



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
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
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/adsedare/export_options.rb', line 20

def make_export_options(project_path = nil, export_path = nil, team_id = nil, options = {})
  raise "Project path is not set" unless project_path
  raise "Export path is not set" unless export_path

  logger.info "Creating export options for project"

  project = Xcodeproj::Project.open(project_path)
  export_options = {
    "method" => "ad-hoc",
    "destination" => "export",
    "signingStyle" => "manual",
    "signingCertificate" => "Apple Distribution",
    "provisioningProfiles" => {},
  }.merge(options)

  project_bundles = []

  project.targets.each do |target|
    target.build_configurations.each do |config|
      team_id ||= config.build_settings["DEVELOPMENT_TEAM"]
      project_bundles << config.build_settings["PRODUCT_BUNDLE_IDENTIFIER"]
    end
  end

  export_options["teamID"] = team_id

  logger.info "Fetching bundles with profiles for team ID '#{team_id}'"

  bundles_with_profiles = AppStoreConnect::Client.get_bundles_with_profiles(project_bundles)
  bundle_by_identifier = {}
  profiles_by_id = {}

  bundles_with_profiles["data"].each do |bundle_id|
    bundle_by_identifier[bundle_id["attributes"]["identifier"]] = bundle_id
  end

  bundles_with_profiles["included"].each do |profile|
    profiles_by_id[profile["id"]] = profile
  end

  project_bundles.each do |bundle_identifier|
    bundle_id = bundle_by_identifier[bundle_identifier]
    unless bundle_id
      logger.warn "Bundle '#{bundle_identifier}' is missing in App Store Connect. Skipping."
      next
    end

    logger.info "Bundle '#{bundle_identifier}' resolved to Bundle ID '#{bundle_id["id"]}'"

    profiles = bundle_id["relationships"]["profiles"]["data"]
    unless profiles
      logger.warn "Profile for Bundle ID '#{bundle_id["id"]}' is missing in App Store Connect. Skipping."
      next
    end

    ad_hoc_profile = nil
    profiles.each do |profile|
      profile_id = profile["id"]
      profile = profiles_by_id[profile_id]

      profile_name = profile["attributes"]["name"]
      # Xcode managed profiles can't be used for signing
      if profile_name.start_with?("iOS Team Ad Hoc Provisioning Profile") || profile_name.start_with?("XC")
        next
      end

      if profile["attributes"]["profileType"] == "IOS_APP_ADHOC" && profile["attributes"]["profileState"] == "ACTIVE"
        ad_hoc_profile = profile
        break
      end
    end

    unless ad_hoc_profile
      logger.warn "Profile for Bundle ID '#{bundle_id["id"]}' is missing in App Store Connect. Skipping."
      next
    end

    logger.info "Profile for Bundle ID '#{bundle_id["id"]}' resolved to Profile '#{ad_hoc_profile["attributes"]["name"]}'"

    profile_name = ad_hoc_profile["attributes"]["name"]

    export_options["provisioningProfiles"][bundle_identifier] = profile_name
  end

  options_plist = Plist::Emit.dump(export_options)
  export_path = File.expand_path(export_path)
  File.write(export_path, options_plist)

  logger.info "Export options created at '#{export_path}'"
end

.patch_project(project_path, team_id = nil) ⇒ void

This method returns an undefined value.

Patch a project with App Store Connect profiles & settings for ad-hoc distribution Will overwrite Team ID in project if provided Expects environment variables to be set:

  • APPSTORE_CONNECT_KEY_ID Key ID from Apple Developer Portal

  • APPSTORE_CONNECT_ISSUER_ID Issuer ID from Apple Developer Portal

  • APPSTORE_CONNECT_KEY P8 key content from Apple Developer Portal

Parameters:

  • project_path (String)

    The path to the Xcode project

  • team_id (String) (defaults to: nil)

    The team ID (optional)



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
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
# File 'lib/adsedare/xcodeproj.rb', line 19

def patch_project(project_path, team_id = nil)
  raise "Project path is not set" unless project_path

  project = Xcodeproj::Project.open(project_path)

  project_bundles = project.targets.map do |target|
    target.build_configurations.map do |config|
      config.build_settings["PRODUCT_BUNDLE_IDENTIFIER"]
    end
  end.flatten.uniq

  bundles_with_profiles = AppStoreConnect::Client.get_bundles_with_profiles(project_bundles)
  bundle_by_identifier = {}
  profiles_by_id = {}

  bundles_with_profiles["data"].each do |bundle_id|
    bundle_by_identifier[bundle_id["attributes"]["identifier"]] = bundle_id
  end

  if bundles_with_profiles["included"]
    bundles_with_profiles["included"].each do |profile|
      profiles_by_id[profile["id"]] = profile
    end
  end

  project.targets.each do |target|
    target.build_configurations.each do |config|
      bundle_identifier = config.build_settings["PRODUCT_BUNDLE_IDENTIFIER"]
      bundle_id = bundle_by_identifier[bundle_identifier]
      unless bundle_id
        logger.warn "Bundle '#{bundle_identifier}' is missing in App Store Connect. Skipping."
        next
      end

      logger.info "Bundle '#{bundle_identifier}' resolved to Bundle ID '#{bundle_id["id"]}'"

      profiles = bundle_id["relationships"]["profiles"]["data"]
      unless profiles
        logger.warn "Profile for Bundle ID '#{bundle_id["id"]}' is missing in App Store Connect. Skipping."
        next
      end

      ad_hoc_profile = nil
      profiles.each do |profile|
        profile_id = profile["id"]
        profile = profiles_by_id[profile_id]

        profile_name = profile["attributes"]["name"]
        # Xcode managed profiles can't be used for signing
        if profile_name.start_with?("iOS Team Ad Hoc Provisioning Profile") || profile_name.start_with?("XC")
          next
        end

        if profile["attributes"]["profileType"] == "IOS_APP_ADHOC" && profile["attributes"]["profileState"] == "ACTIVE"
          ad_hoc_profile = profile
          break
        end
      end

      unless ad_hoc_profile
        logger.warn "Profile for Bundle ID '#{bundle_id["id"]}' is missing in App Store Connect. Skipping."
        next
      end

      config.build_settings["CODE_SIGN_IDENTITY"] = "iPhone Distribution"
      config.build_settings["CODE_SIGN_STYLE"] = "Manual"
      if team_id
        config.build_settings["DEVELOPMENT_TEAM"] = team_id
      end
      config.build_settings["PROVISIONING_PROFILE_SPECIFIER"] = ad_hoc_profile["attributes"]["name"]
    end
  end

  project.save
end

.renew_bundle_id(bundle_id, team_id, entitlements_path) ⇒ Object



170
171
172
173
174
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
# File 'lib/adsedare.rb', line 170

def renew_bundle_id(bundle_id, team_id, entitlements_path)
  bundle_info = Starship::Client.get_bundle_info(bundle_id, team_id)
  bundle_identifier = bundle_info["data"]["attributes"]["identifier"]

  logger.info "Checking capabilities for bundle '#{bundle_identifier}'"

  capabilities = parse_entitlements(entitlements_path)

  need_update = false

  capabilities.each do |capability|
    if !capability.check?(bundle_info)
      logger.warn "Bundle '#{bundle_identifier}' is missing capability '#{capability.type}'."
      need_update = true
    end
  end

  if need_update
    logger.warn "Bundle '#{bundle_identifier}' is missing one or more capabilities."
    # You can't remove IN_APP_PURCHASE capability for some reason
    new_capabilities = capabilities + [SimpleCapability.new("IN_APP_PURCHASE")]
    new_capabilities = new_capabilities.map { |capability| capability.to_bundle_capability(bundle_info, team_id) }

    Starship::Client.patch_bundle(bundle_info, team_id, new_capabilities)

    logger.info "Bundle '#{bundle_identifier}' capabilities updated."
  else
    logger.info "Bundle '#{bundle_identifier}' capabilities are up to date."
  end
end

.renew_profiles(project_path = nil, certificate_id = nil, team_id = nil) ⇒ void

This method returns an undefined value.

Renew profiles for a project Expects environment variables to be set:

  • APPLE_DEVELOPER_USERNAME Apple Developer Portal username

  • APPLE_DEVELOPER_PASSWORD Apple Developer Portal password

Parameters:

  • project_path (String) (defaults to: nil)

    The path to the Xcode project

  • certificate_id (String) (defaults to: nil)

    The certificate ID

  • team_id (String) (defaults to: nil)

    The team ID (optional)



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
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
99
100
101
# File 'lib/adsedare.rb', line 36

def renew_profiles(project_path = nil, certificate_id = nil, team_id = nil)
  raise "Project path is not set" unless project_path
  raise "Certificate ID is not set" unless certificate_id

  project = Xcodeproj::Project.open(project_path)
  project_dir = File.dirname(project_path)

  bundle_entitlements = {}

  project.targets.each do |target|
    target.build_configurations.each do |config|
      bundle_identifier = config.build_settings["PRODUCT_BUNDLE_IDENTIFIER"]
      entitlements_path = config.build_settings["CODE_SIGN_ENTITLEMENTS"]

      # If team_id is not set, use the first one from the project
      team_id ||= config.build_settings["DEVELOPMENT_TEAM"]

      if entitlements_path
        full_entitlements_path = File.join(project_dir, entitlements_path)
        bundle_entitlements[bundle_identifier] = full_entitlements_path
      end
    end
  end

  bundle_by_identifier = get_bundle_map(team_id)
  profiles_by_bundle = get_profiles_map(team_id)

  bundle_entitlements.each do |bundle_identifier, entitlements_path|
    bundle_id = bundle_by_identifier[bundle_identifier]
    unless bundle_id
      logger.warn "Bundle '#{bundle_identifier}' is missing in Apple Developer portal. Will create."
      bundle_id = Starship::Client.create_bundle(
        bundle_identifier,
        team_id,
        # You cannot create bundle without this capability
        [SimpleCapability.new("IN_APP_PURCHASE").to_bundle_capability(nil, nil)]
      )["data"]["id"]
      bundle_by_identifier[bundle_identifier] = bundle_id
      logger.info "Bundle '#{bundle_identifier}' created with ID '#{bundle_id}'"
    else
      logger.info "Bundle '#{bundle_identifier}' resolved to Bundle ID '#{bundle_id}'"
    end

    renew_bundle_id(bundle_id, team_id, entitlements_path)

    profile = profiles_by_bundle[bundle_id]
    unless profile
      logger.warn "Profile for Bundle ID '#{bundle_id}' is missing in Apple Developer portal. Will create."
      devices = get_devices(team_id)
      profile_id = Starship::Client.create_provisioning_profile(
        team_id,
        bundle_id,
        bundle_identifier,
        certificate_id,
        devices
      )["data"]["id"]
      profiles_by_bundle[bundle_id] = Starship::Client.get_provisioning_profile(profile_id, team_id)
      profile = profiles_by_bundle[bundle_id]
      logger.info "Profile for Bundle ID '#{bundle_id}' created with ID '#{profile_id}'"
    else
      logger.info "Bundle ID '#{bundle_id}' resolved to Profile '#{profile["provisioningProfile"]["name"]}'"
    end

    renew_provisioning_profile(profile, team_id)
  end
end

.renew_provisioning_profile(profile, team_id) ⇒ Object



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/adsedare.rb', line 136

def renew_provisioning_profile(profile, team_id)
  devices = get_devices(team_id)
  deviceIds = devices.map { |device| device["id"] }

  profileDeviceIds = profile["provisioningProfile"]["devices"].map { 
    |device| device["deviceId"] 
  }

  need_update = false

  deviceIds.each do |deviceId|
    if !profileDeviceIds.include?(deviceId)
      need_update = true
      break
    end
  end

  logger.info "Profile '#{profile["provisioningProfile"]["name"]}' status: '#{profile["provisioningProfile"]["status"]}'"

  if profile["provisioningProfile"]["status"] != "Active"
    need_update = true
  end

  if need_update
    logger.warn "Profile '#{profile["provisioningProfile"]["name"]}' is missing one or more devices."

    Starship::Client.regen_provisioning_profile(profile, team_id, deviceIds)

    logger.info "Profile '#{profile["provisioningProfile"]["name"]}' updated."
  else
    logger.info "Profile '#{profile["provisioningProfile"]["name"]}' is up to date."
  end
end