spaceship 0.0.15 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/lib/assets/languageMapping.json +224 -0
  3. data/lib/spaceship.rb +20 -63
  4. data/lib/spaceship/base.rb +71 -14
  5. data/lib/spaceship/client.rb +9 -274
  6. data/lib/spaceship/launcher.rb +1 -1
  7. data/lib/spaceship/portal/app.rb +125 -0
  8. data/lib/spaceship/portal/certificate.rb +273 -0
  9. data/lib/spaceship/portal/device.rb +102 -0
  10. data/lib/spaceship/portal/portal.rb +6 -0
  11. data/lib/spaceship/portal/portal_base.rb +13 -0
  12. data/lib/spaceship/portal/portal_client.rb +289 -0
  13. data/lib/spaceship/portal/provisioning_profile.rb +369 -0
  14. data/lib/spaceship/portal/spaceship.rb +94 -0
  15. data/lib/spaceship/{ui → portal/ui}/select_team.rb +0 -0
  16. data/lib/spaceship/tunes/app_screenshot.rb +28 -0
  17. data/lib/spaceship/tunes/app_status.rb +63 -0
  18. data/lib/spaceship/tunes/app_submission.rb +149 -0
  19. data/lib/spaceship/tunes/app_version.rb +337 -0
  20. data/lib/spaceship/tunes/application.rb +253 -0
  21. data/lib/spaceship/tunes/build.rb +128 -0
  22. data/lib/spaceship/tunes/build_train.rb +79 -0
  23. data/lib/spaceship/tunes/language_converter.rb +44 -0
  24. data/lib/spaceship/tunes/language_item.rb +54 -0
  25. data/lib/spaceship/tunes/processing_build.rb +30 -0
  26. data/lib/spaceship/tunes/spaceship.rb +26 -0
  27. data/lib/spaceship/tunes/tester.rb +177 -0
  28. data/lib/spaceship/tunes/tunes.rb +12 -0
  29. data/lib/spaceship/tunes/tunes_base.rb +15 -0
  30. data/lib/spaceship/tunes/tunes_client.rb +360 -0
  31. data/lib/spaceship/version.rb +1 -1
  32. metadata +27 -7
  33. data/lib/spaceship/app.rb +0 -125
  34. data/lib/spaceship/certificate.rb +0 -271
  35. data/lib/spaceship/device.rb +0 -100
  36. data/lib/spaceship/provisioning_profile.rb +0 -367
@@ -0,0 +1,273 @@
1
+ require 'openssl'
2
+
3
+ module Spaceship
4
+ module Portal
5
+ # Represents a certificate from the Apple Developer Portal.
6
+ #
7
+ # This can either be a code signing identity or a push profile
8
+ class Certificate < PortalBase
9
+ # @return (String) The ID given from the developer portal. You'll probably not need it.
10
+ # @example
11
+ # "P577TH3PAA"
12
+ attr_accessor :id
13
+
14
+ # @return (String) The name of the certificate
15
+ # @example Company
16
+ # "SunApps GmbH"
17
+ # @example Push Profile
18
+ # "com.krausefx.app"
19
+ attr_accessor :name
20
+
21
+ # @return (String) Status of the certificate
22
+ # @example
23
+ # "Issued"
24
+ attr_accessor :status
25
+
26
+ # @return (Date) The date and time when the certificate was created
27
+ # @example
28
+ # 2015-04-01 21:24:00 UTC
29
+ attr_accessor :created
30
+
31
+ # @return (Date) The date and time when the certificate will expire
32
+ # @example
33
+ # 2016-04-01 21:24:00 UTC
34
+ attr_accessor :expires
35
+
36
+ # @return (String) The owner type that defines if it's a push profile
37
+ # or a code signing identity
38
+ #
39
+ # @example Code Signing Identity
40
+ # "team"
41
+ # @example Push Certificate
42
+ # "bundle"
43
+ attr_accessor :owner_type
44
+
45
+ # @return (String) The name of the owner
46
+ #
47
+ # @example Code Signing Identity (usually the company name)
48
+ # "SunApps Gmbh"
49
+ # @example Push Certificate (the name of your App ID)
50
+ # "Awesome App"
51
+ attr_accessor :owner_name
52
+
53
+ # @return (String) The ID of the owner, that can be used to
54
+ # fetch more information
55
+ # @example
56
+ # "75B83SPLAA"
57
+ attr_accessor :owner_id
58
+
59
+ # Indicates the type of this certificate
60
+ # which is automatically used to determine the class of
61
+ # the certificate. Available values listed in CERTIFICATE_TYPE_IDS
62
+ # @return (String) The type of the certificate
63
+ # @example Production Certificate
64
+ # "R58UK2EWSO"
65
+ # @example Development Certificate
66
+ # "5QPB9NHCEI"
67
+ attr_accessor :type_display_id
68
+
69
+ attr_mapping({
70
+ 'certificateId' => :id,
71
+ 'name' => :name,
72
+ 'statusString' => :status,
73
+ 'dateCreated' => :created,
74
+ 'expirationDate' => :expires,
75
+ 'ownerType' => :owner_type,
76
+ 'ownerName' => :owner_name,
77
+ 'ownerId' => :owner_id,
78
+ 'certificateTypeDisplayId' => :type_display_id
79
+ })
80
+
81
+ #####################################################
82
+ # Certs are not associated with apps
83
+ #####################################################
84
+
85
+ # A development code signing certificate used for development environment
86
+ class Development < Certificate; end
87
+
88
+ # A production code signing certificate used for distribution environment
89
+ class Production < Certificate; end
90
+
91
+ # An In House code signing certificate used for enterprise distributions
92
+ class InHouse < Certificate; end
93
+
94
+ #####################################################
95
+ # Certs that are specific for one app
96
+ #####################################################
97
+
98
+ # Abstract class for push certificates. Check out the subclasses
99
+ # DevelopmentPush, ProductionPush, WebsitePush and VoipPush
100
+ class PushCertificate < Certificate; end
101
+
102
+ # A push notification certificate for development environment
103
+ class DevelopmentPush < PushCertificate; end
104
+
105
+ # A push notification certificate for production environment
106
+ class ProductionPush < PushCertificate; end
107
+
108
+ # A push notification certificate for websites
109
+ class WebsitePush < PushCertificate; end
110
+
111
+ # A push notification certificate for the VOIP environment
112
+ class VoipPush < PushCertificate; end
113
+
114
+ # Passbook certificate
115
+ class Passbook < Certificate; end
116
+
117
+ # ApplePay certificate
118
+ class ApplePay < Certificate; end
119
+
120
+ CERTIFICATE_TYPE_IDS = {
121
+ "5QPB9NHCEI" => Development,
122
+ "R58UK2EWSO" => Production,
123
+ "9RQEK7MSXA" => InHouse,
124
+ "LA30L5BJEU" => Certificate,
125
+ "BKLRAVXMGM" => DevelopmentPush,
126
+ "3BQKVH9I2X" => ProductionPush,
127
+ "Y3B2F3TYSI" => Passbook,
128
+ "3T2ZP62QW8" => WebsitePush,
129
+ "E5D663CMZW" => WebsitePush,
130
+ "4APLUP237T" => ApplePay
131
+ }
132
+
133
+ #class methods
134
+ class << self
135
+ # Create a new code signing request that can be used to
136
+ # generate a new certificate
137
+ # @example
138
+ # Create a new certificate signing request
139
+ # csr, pkey = Spaceship.certificate.create_certificate_signing_request
140
+ #
141
+ # # Use the signing request to create a new distribution certificate
142
+ # Spaceship.certificate.production.create!(csr: csr)
143
+ def create_certificate_signing_request
144
+ key = OpenSSL::PKey::RSA.new 2048
145
+ csr = OpenSSL::X509::Request.new
146
+ csr.version = 0
147
+ csr.subject = OpenSSL::X509::Name.new([
148
+ ['CN', 'PEM', OpenSSL::ASN1::UTF8STRING]
149
+ ])
150
+ csr.public_key = key.public_key
151
+ csr.sign(key, OpenSSL::Digest::SHA1.new)
152
+ return [csr, key]
153
+ end
154
+
155
+ # Create a new object based on a hash.
156
+ # This is used to create a new object based on the server response.
157
+ def factory(attrs)
158
+ # Example:
159
+ # => {"name"=>"iOS Distribution: SunApps GmbH",
160
+ # "certificateId"=>"XC5PH8DAAA",
161
+ # "serialNumber"=>"797E732CCE8B7AAA",
162
+ # "status"=>"Issued",
163
+ # "statusCode"=>0,
164
+ # "expirationDate"=>#<DateTime: 2015-11-25T22:45:50+00:00 ((2457352j,81950s,0n),+0s,2299161j)>,
165
+ # "certificatePlatform"=>"ios",
166
+ # "certificateType"=>
167
+ # {"certificateTypeDisplayId"=>"R58UK2EAAA",
168
+ # "name"=>"iOS Distribution",
169
+ # "platform"=>"ios",
170
+ # "permissionType"=>"distribution",
171
+ # "distributionType"=>"store",
172
+ # "distributionMethod"=>"app",
173
+ # "ownerType"=>"team",
174
+ # "daysOverlap"=>364,
175
+ # "maxActive"=>2}}
176
+
177
+ if attrs['certificateType']
178
+ # On some accounts this is nested, so we need to flatten it
179
+ attrs.merge!(attrs['certificateType'])
180
+ attrs.delete('certificateType')
181
+ end
182
+
183
+ # Parse the dates
184
+ attrs['expirationDate'] = (Time.parse(attrs['expirationDate']) rescue attrs['expirationDate'])
185
+ attrs['dateCreated'] = (Time.parse(attrs['dateCreated']) rescue attrs['dateCreated'])
186
+
187
+ # Here we go
188
+ klass = CERTIFICATE_TYPE_IDS[attrs['certificateTypeDisplayId']]
189
+ klass ||= Certificate
190
+ klass.client = @client
191
+ klass.new(attrs)
192
+ end
193
+
194
+ # @return (Array) Returns all certificates of this account.
195
+ # If this is called from a subclass of Certificate, this will
196
+ # only include certificates matching the current type.
197
+ def all
198
+ if (self == Certificate) # are we the base-class?
199
+ types = CERTIFICATE_TYPE_IDS.keys
200
+ else
201
+ types = [CERTIFICATE_TYPE_IDS.key(self)]
202
+ end
203
+
204
+ client.certificates(types).map do |cert|
205
+ factory(cert)
206
+ end
207
+ end
208
+
209
+ # @return (Certificate) Find a certificate based on the ID of the certificate.
210
+ def find(certificate_id)
211
+ all.find do |c|
212
+ c.id == certificate_id
213
+ end
214
+ end
215
+
216
+ # Generate a new certificate based on a code certificate signing request
217
+ # @param csr (OpenSSL::X509::Request) (required): The certificate signing request to use. Get one using
218
+ # `create_certificate_signing_request`
219
+ # @param bundle_id (String) (optional): The app identifier this certificate is for.
220
+ # This value is only needed if you create a push profile. For normal code signing
221
+ # certificates, you must only pass a certificate signing request.
222
+ # @example
223
+ # # Create a new certificate signing request
224
+ # csr, pkey = Spaceship::Certificate.create_certificate_signing_request
225
+ #
226
+ # # Use the signing request to create a new distribution certificate
227
+ # Spaceship::Certificate::Production.create!(csr: csr)
228
+ # @return (Device): The newly created device
229
+ def create!(csr: nil, bundle_id: nil)
230
+ type = CERTIFICATE_TYPE_IDS.key(self)
231
+
232
+ # look up the app_id by the bundle_id
233
+ if bundle_id
234
+ app = Spaceship::App.find(bundle_id)
235
+ raise "Could not find app with bundle id '#{bundle_id}'" unless app
236
+ app_id = app.app_id
237
+ end
238
+
239
+ # ensure csr is a OpenSSL::X509::Request
240
+ csr = OpenSSL::X509::Request.new(csr) if csr.is_a?(String)
241
+
242
+ # if this succeeds, we need to save the .cer and the private key in keychain access or wherever they go in linux
243
+ response = client.create_certificate!(type, csr.to_pem, app_id)
244
+ # munge the response to make it work for the factory
245
+ response['certificateTypeDisplayId'] = response['certificateType']['certificateTypeDisplayId']
246
+ self.new(response)
247
+ end
248
+ end
249
+
250
+ # instance methods
251
+
252
+ # @return (String) Download the raw data of the certificate without parsing
253
+ def download_raw
254
+ client.download_certificate(id, type_display_id)
255
+ end
256
+
257
+ # @return (OpenSSL::X509::Certificate) Downloads and parses the certificate
258
+ def download
259
+ OpenSSL::X509::Certificate.new(download_raw)
260
+ end
261
+
262
+ # Revoke the certificate. You shouldn't use this method probably.
263
+ def revoke!
264
+ client.revoke_certificate!(id, type_display_id)
265
+ end
266
+
267
+ # @return (Bool): Is this certificate a push profile for apps?
268
+ def is_push?
269
+ self.kind_of?PushCertificate
270
+ end
271
+ end
272
+ end
273
+ end
@@ -0,0 +1,102 @@
1
+ module Spaceship
2
+ module Portal
3
+ # Represents a device from the Apple Developer Portal
4
+ class Device < PortalBase
5
+ # @return (String) The ID given from the developer portal. You'll probably not need it.
6
+ # @example
7
+ # "XJXGVS46MW"
8
+ attr_accessor :id
9
+
10
+ # @return (String) The name of the device
11
+ # @example
12
+ # "Felix Krause's iPhone 6"
13
+ attr_accessor :name
14
+
15
+ # @return (String) The UDID of the device
16
+ # @example
17
+ # "4c24a7ee5caaa4847f49aaab2d87483053f53b65"
18
+ attr_accessor :udid
19
+
20
+ # @return (String) The platform of the device. This is probably always "ios"
21
+ # @example
22
+ # "ios"
23
+ attr_accessor :platform
24
+
25
+ # @return (String) Status of the device. Probably always "c"
26
+ # @example
27
+ # "c"
28
+ attr_accessor :status
29
+
30
+ attr_mapping({
31
+ 'deviceId' => :id,
32
+ 'name' => :name,
33
+ 'deviceNumber' => :udid,
34
+ 'devicePlatform' => :platform,
35
+ 'status' => :status
36
+ })
37
+
38
+ class << self
39
+ # Create a new object based on a hash.
40
+ # This is used to create a new object based on the server response.
41
+ def factory(attrs)
42
+ self.new(attrs)
43
+ end
44
+
45
+ # @return (Array) Returns all devices registered for this account
46
+ def all
47
+ client.devices.map { |device| self.factory(device) }
48
+ end
49
+
50
+ # @return (Device) Find a device based on the ID of the device. *Attention*:
51
+ # This is *not* the UDID. nil if no device was found.
52
+ def find(device_id)
53
+ all.find do |device|
54
+ device.id == device_id
55
+ end
56
+ end
57
+
58
+ # @return (Device) Find a device based on the UDID of the device. nil if no device was found.
59
+ def find_by_udid(device_udid)
60
+ all.find do |device|
61
+ device.udid == device_udid
62
+ end
63
+ end
64
+
65
+ # @return (Device) Find a device based on its name. nil if no device was found.
66
+ def find_by_name(device_name)
67
+ all.find do |device|
68
+ device.name == device_name
69
+ end
70
+ end
71
+
72
+ # Register a new device to this account
73
+ # @param name (String) (required): The name of the new device
74
+ # @param udid (String) (required): The UDID of the new device
75
+ # @example
76
+ # Spaceship.device.create!(name: "Felix Krause's iPhone 6", udid: "4c24a7ee5caaa4847f49aaab2d87483053f53b65")
77
+ # @return (Device): The newly created device
78
+ def create!(name: nil, udid: nil)
79
+ # Check whether the user has passed in a UDID and a name
80
+ unless (udid and name)
81
+ raise "You cannot create a device without a device_id (UDID) and name"
82
+ end
83
+
84
+ # Find the device by UDID, raise an exception if it already exists
85
+ if self.find_by_udid(udid)
86
+ raise "The device UDID '#{udid}' already exists on this team."
87
+ end
88
+
89
+ # Find the device by name, raise an exception if it already exists
90
+ if self.find_by_name(name)
91
+ raise "The device name '#{name}' already exists on this team, use different one."
92
+ end
93
+
94
+ device = client.create_device!(name, udid)
95
+
96
+ # Update self with the new device
97
+ self.new(device)
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,6 @@
1
+ require 'spaceship/portal/portal_base'
2
+ require 'spaceship/portal/app'
3
+ require 'spaceship/portal/certificate'
4
+ require 'spaceship/portal/device'
5
+ require 'spaceship/portal/provisioning_profile'
6
+ require 'spaceship/portal/portal_client'
@@ -0,0 +1,13 @@
1
+ module Spaceship
2
+ class PortalBase < Spaceship::Base
3
+ class << self
4
+ def client
5
+ (
6
+ @client or
7
+ Spaceship::Portal.client or
8
+ raise "Please login using `Spaceship::Portal.login('user', 'password')`"
9
+ )
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,289 @@
1
+ module Spaceship
2
+ class PortalClient < Spaceship::Client
3
+
4
+ #####################################################
5
+ # @!group Init and Login
6
+ #####################################################
7
+
8
+ def self.hostname
9
+ "https://developer.apple.com/services-account/#{PROTOCOL_VERSION}/"
10
+ end
11
+
12
+ # Fetches the latest API Key from the Apple Dev Portal
13
+ def api_key
14
+ cache_path = "/tmp/spaceship_api_key.txt"
15
+ begin
16
+ cached = File.read(cache_path)
17
+ rescue Errno::ENOENT
18
+ end
19
+ return cached if cached
20
+
21
+ landing_url = "https://developer.apple.com/membercenter/index.action"
22
+ logger.info("GET: " + landing_url)
23
+ headers = @client.get(landing_url).headers
24
+ results = headers['location'].match(/.*appIdKey=(\h+)/)
25
+ if results.length > 1
26
+ api_key = results[1]
27
+ File.write(cache_path, api_key)
28
+ return api_key
29
+ else
30
+ raise "Could not find latest API Key from the Dev Portal"
31
+ end
32
+ end
33
+
34
+ def send_login_request(user, password)
35
+ response = request(:post, "https://idmsa.apple.com/IDMSWebAuth/authenticate", {
36
+ appleId: user,
37
+ accountPassword: password,
38
+ appIdKey: api_key
39
+ })
40
+
41
+ if response['Set-Cookie'] =~ /myacinfo=(\w+);/
42
+ @cookie = "myacinfo=#{$1};"
43
+ return @client
44
+ else
45
+ # User Credentials are wrong
46
+ raise InvalidUserCredentialsError.new(response)
47
+ end
48
+ end
49
+
50
+ # @return (Array) A list of all available teams
51
+ def teams
52
+ r = request(:post, 'account/listTeams.action')
53
+ parse_response(r, 'teams')
54
+ end
55
+
56
+ # @return (String) The currently selected Team ID
57
+ def team_id
58
+ return @current_team_id if @current_team_id
59
+
60
+ if teams.count > 1
61
+ puts "The current user is in #{teams.count} teams. Pass a team ID or call `select_team` to choose a team. Using the first one for now."
62
+ end
63
+ @current_team_id ||= teams[0]['teamId']
64
+ end
65
+
66
+ # Shows a team selection for the user in the terminal. This should not be
67
+ # called on CI systems
68
+ def select_team
69
+ @current_team_id = self.UI.select_team
70
+ end
71
+
72
+ # Set a new team ID which will be used from now on
73
+ def team_id=(team_id)
74
+ @current_team_id = team_id
75
+ end
76
+
77
+ # @return (Hash) Fetches all information of the currently used team
78
+ def team_information
79
+ teams.find do |t|
80
+ t['teamId'] == team_id
81
+ end
82
+ end
83
+
84
+ # Is the current session from an Enterprise In House account?
85
+ def in_house?
86
+ # TODO: move to portal
87
+ return @in_house unless @in_house.nil?
88
+ @in_house = (team_information['type'] == 'In-House')
89
+ end
90
+
91
+
92
+ #####################################################
93
+ # @!group Apps
94
+ #####################################################
95
+
96
+ def apps
97
+ paging do |page_number|
98
+ r = request(:post, 'account/ios/identifiers/listAppIds.action', {
99
+ teamId: team_id,
100
+ pageNumber: page_number,
101
+ pageSize: page_size,
102
+ sort: 'name=asc'
103
+ })
104
+ parse_response(r, 'appIds')
105
+ end
106
+ end
107
+
108
+ def details_for_app(app)
109
+ r = request(:post, 'account/ios/identifiers/getAppIdDetail.action', {
110
+ teamId: team_id,
111
+ appIdId: app.app_id
112
+ })
113
+ parse_response(r, 'appId')
114
+ end
115
+
116
+ def create_app!(type, name, bundle_id)
117
+ ident_params = case type.to_sym
118
+ when :explicit
119
+ {
120
+ type: 'explicit',
121
+ explicitIdentifier: bundle_id,
122
+ appIdentifierString: bundle_id,
123
+ push: 'on',
124
+ inAppPurchase: 'on',
125
+ gameCenter: 'on'
126
+ }
127
+ when :wildcard
128
+ {
129
+ type: 'wildcard',
130
+ wildcardIdentifier: bundle_id,
131
+ appIdentifierString: bundle_id
132
+ }
133
+ end
134
+
135
+ params = {
136
+ appIdName: name,
137
+ teamId: team_id
138
+ }
139
+
140
+ params.merge!(ident_params)
141
+
142
+ r = request(:post, 'account/ios/identifiers/addAppId.action', params)
143
+ parse_response(r, 'appId')
144
+ end
145
+
146
+ def delete_app!(app_id)
147
+ r = request(:post, 'account/ios/identifiers/deleteAppId.action', {
148
+ teamId: team_id,
149
+ appIdId: app_id
150
+ })
151
+ parse_response(r)
152
+ end
153
+
154
+ #####################################################
155
+ # @!group Devices
156
+ #####################################################
157
+
158
+ def devices
159
+ paging do |page_number|
160
+ r = request(:post, 'account/ios/device/listDevices.action', {
161
+ teamId: team_id,
162
+ pageNumber: page_number,
163
+ pageSize: page_size,
164
+ sort: 'name=asc'
165
+ })
166
+ parse_response(r, 'devices')
167
+ end
168
+ end
169
+
170
+ def create_device!(device_name, device_id)
171
+ r = request(:post) do |r|
172
+ r.url "https://developerservices2.apple.com/services/#{PROTOCOL_VERSION}/ios/addDevice.action"
173
+ r.params = {
174
+ teamId: team_id,
175
+ deviceNumber: device_id,
176
+ name: device_name
177
+ }
178
+ end
179
+
180
+ parse_response(r, 'device')
181
+ end
182
+
183
+ #####################################################
184
+ # @!group Certificates
185
+ #####################################################
186
+
187
+ def certificates(types)
188
+ paging do |page_number|
189
+ r = request(:post, 'account/ios/certificate/listCertRequests.action', {
190
+ teamId: team_id,
191
+ types: types.join(','),
192
+ pageNumber: page_number,
193
+ pageSize: page_size,
194
+ sort: 'certRequestStatusCode=asc'
195
+ })
196
+ parse_response(r, 'certRequests')
197
+ end
198
+ end
199
+
200
+ def create_certificate!(type, csr, app_id = nil)
201
+ r = request(:post, 'account/ios/certificate/submitCertificateRequest.action', {
202
+ teamId: team_id,
203
+ type: type,
204
+ csrContent: csr,
205
+ appIdId: app_id #optional
206
+ })
207
+ parse_response(r, 'certRequest')
208
+ end
209
+
210
+ def download_certificate(certificate_id, type)
211
+ {type: type, certificate_id: certificate_id}.each { |k, v| raise "#{k} must not be nil" if v.nil? }
212
+
213
+ r = request(:post, 'https://developer.apple.com/account/ios/certificate/certificateContentDownload.action', {
214
+ teamId: team_id,
215
+ displayId: certificate_id,
216
+ type: type
217
+ })
218
+ parse_response(r)
219
+ end
220
+
221
+ def revoke_certificate!(certificate_id, type)
222
+ r = request(:post, 'account/ios/certificate/revokeCertificate.action', {
223
+ teamId: team_id,
224
+ certificateId: certificate_id,
225
+ type: type
226
+ })
227
+ parse_response(r, 'certRequests')
228
+ end
229
+
230
+ #####################################################
231
+ # @!group Provisioning Profiles
232
+ #####################################################
233
+
234
+ def provisioning_profiles
235
+ r = request(:post) do |r|
236
+ r.url "https://developerservices2.apple.com/services/#{PROTOCOL_VERSION}/ios/listProvisioningProfiles.action"
237
+ r.params = {
238
+ teamId: team_id,
239
+ includeInactiveProfiles: true,
240
+ onlyCountLists: true,
241
+ }
242
+ end
243
+
244
+ parse_response(r, 'provisioningProfiles')
245
+ end
246
+
247
+ def create_provisioning_profile!(name, distribution_method, app_id, certificate_ids, device_ids)
248
+ r = request(:post, 'account/ios/profile/createProvisioningProfile.action', {
249
+ teamId: team_id,
250
+ provisioningProfileName: name,
251
+ appIdId: app_id,
252
+ distributionType: distribution_method,
253
+ certificateIds: certificate_ids,
254
+ deviceIds: device_ids
255
+ })
256
+ parse_response(r, 'provisioningProfile')
257
+ end
258
+
259
+ def download_provisioning_profile(profile_id)
260
+ r = request(:get, 'https://developer.apple.com/account/ios/profile/profileContentDownload.action', {
261
+ teamId: team_id,
262
+ displayId: profile_id
263
+ })
264
+ parse_response(r)
265
+ end
266
+
267
+ def delete_provisioning_profile!(profile_id)
268
+ r = request(:post, 'account/ios/profile/deleteProvisioningProfile.action', {
269
+ teamId: team_id,
270
+ provisioningProfileId: profile_id
271
+ })
272
+ parse_response(r)
273
+ end
274
+
275
+ def repair_provisioning_profile!(profile_id, name, distribution_method, app_id, certificate_ids, device_ids)
276
+ r = request(:post, 'account/ios/profile/regenProvisioningProfile.action', {
277
+ teamId: team_id,
278
+ provisioningProfileId: profile_id,
279
+ provisioningProfileName: name,
280
+ appIdId: app_id,
281
+ distributionType: distribution_method,
282
+ certificateIds: certificate_ids.first, # we are most of the times only allowed to pass one
283
+ deviceIds: device_ids
284
+ })
285
+
286
+ parse_response(r, 'provisioningProfile')
287
+ end
288
+ end
289
+ end