spaceship 0.0.15 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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