cupertino 0.7.1 → 0.8.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.
- checksums.yaml +7 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +13 -13
- data/README.md +48 -123
- data/cupertino.gemspec +4 -3
- data/lib/cupertino/provisioning_portal/agent.rb +144 -199
- data/lib/cupertino/provisioning_portal/commands/certificates.rb +6 -6
- data/lib/cupertino/provisioning_portal/commands/devices.rb +1 -1
- data/lib/cupertino/provisioning_portal/commands/login.rb +2 -2
- data/lib/cupertino/provisioning_portal/commands/logout.rb +2 -2
- data/lib/cupertino/provisioning_portal/commands/profiles.rb +2 -2
- data/lib/cupertino/provisioning_portal/commands.rb +0 -1
- data/lib/cupertino/provisioning_portal/helpers.rb +13 -4
- data/lib/cupertino/provisioning_portal.rb +7 -4
- data/lib/cupertino.rb +1 -2
- metadata +35 -62
- data/cupertino-0.7.0.gem +0 -0
- data/lib/cupertino/provisioning_portal/commands/pass_type_ids.rb +0 -139
@@ -11,12 +11,12 @@ module Cupertino
|
|
11
11
|
super
|
12
12
|
self.user_agent_alias = 'Mac Safari'
|
13
13
|
|
14
|
-
pw = Security::InternetPassword.find(:server => Cupertino::
|
14
|
+
pw = Security::InternetPassword.find(:server => Cupertino::ProvisioningPortal::HOST)
|
15
15
|
@username, @password = pw.attributes['acct'], pw.password if pw
|
16
16
|
end
|
17
17
|
|
18
18
|
def get(uri, parameters = [], referer = nil, headers = {})
|
19
|
-
uri = ::File.join("https://#{Cupertino::
|
19
|
+
uri = ::File.join("https://#{Cupertino::ProvisioningPortal::HOST}", uri) unless /^https?/ === uri
|
20
20
|
|
21
21
|
3.times do
|
22
22
|
super(uri, parameters, referer, headers)
|
@@ -24,12 +24,12 @@ module Cupertino
|
|
24
24
|
return page unless page.respond_to?(:title)
|
25
25
|
|
26
26
|
case page.title
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
27
|
+
when /Sign in with your Apple ID/
|
28
|
+
login! and redo
|
29
|
+
when /Select Team/
|
30
|
+
select_team! and redo
|
31
|
+
else
|
32
|
+
return page
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
@@ -38,87 +38,106 @@ module Cupertino
|
|
38
38
|
|
39
39
|
def list_certificates(type = :development)
|
40
40
|
url = case type
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
41
|
+
when :development
|
42
|
+
"https://developer.apple.com/account/ios/certificate/certificateList.action?type=development"
|
43
|
+
when :distribution
|
44
|
+
"https://developer.apple.com/account/ios/certificate/certificateList.action?type=distribution"
|
45
|
+
else
|
46
|
+
raise ArgumentError, "Certificate type must be :development or :distribution"
|
47
47
|
end
|
48
48
|
|
49
49
|
get(url)
|
50
50
|
|
51
|
+
regex = /certificateDataURL = "([^"]*)"/
|
52
|
+
certificate_data_url = (page.body.match regex or raise UnexpectedContentError)[1]
|
53
|
+
|
54
|
+
regex = /certificateRequestTypes = "([^"]*)"/
|
55
|
+
certificate_request_types = (page.body.match regex or raise UnexpectedContentError)[1]
|
56
|
+
|
57
|
+
regex = /certificateStatuses = "([^"]*)"/
|
58
|
+
certificate_statuses = (page.body.match regex or raise UnexpectedContentError)[1]
|
59
|
+
|
60
|
+
certificate_data_url += certificate_request_types + certificate_statuses
|
61
|
+
|
62
|
+
get(certificate_data_url)
|
63
|
+
certificate_data = page.content
|
64
|
+
parsed_certificate_data = JSON.parse(certificate_data)
|
65
|
+
|
51
66
|
certificates = []
|
52
|
-
|
67
|
+
parsed_certificate_data['certRequests'].each do |row|
|
53
68
|
certificate = Certificate.new
|
54
|
-
certificate.name = row
|
69
|
+
certificate.name = row['name']
|
55
70
|
certificate.type = type
|
56
|
-
certificate.
|
57
|
-
certificate.expiration_date = row
|
58
|
-
certificate.status = row
|
71
|
+
certificate.download_url = "https://developer.apple.com/account/ios/certificate/certificateContentDownload.action?displayId=#{row['certificateId']}&type=#{row['certificateTypeDisplayId']}"
|
72
|
+
certificate.expiration_date = row['expirationDateString']
|
73
|
+
certificate.status = row['statusString']
|
59
74
|
certificates << certificate
|
60
75
|
end
|
76
|
+
|
61
77
|
certificates
|
62
78
|
end
|
63
79
|
|
64
80
|
def download_certificate(certificate)
|
65
81
|
list_certificates(certificate.type)
|
66
82
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
download_url = row.at_xpath('td[@class="last"]/a')['href'].to_s.strip rescue nil
|
72
|
-
|
73
|
-
self.pluggable_parser.default = Mechanize::Download
|
74
|
-
download = get(download_url)
|
75
|
-
download.save
|
76
|
-
return download.filename
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
return nil
|
83
|
+
self.pluggable_parser.default = Mechanize::Download
|
84
|
+
download = get(certificate.download_url)
|
85
|
+
download.save
|
86
|
+
download.filename
|
81
87
|
end
|
82
88
|
|
83
89
|
def list_devices
|
84
|
-
get(
|
90
|
+
get('https://developer.apple.com/account/ios/device/deviceList.action')
|
91
|
+
|
92
|
+
regex = /deviceDataURL = "([^"]*)"/
|
93
|
+
device_data_url = (page.body.match regex or raise UnexpectedContentError)[1]
|
94
|
+
|
95
|
+
get(device_data_url)
|
96
|
+
device_data = page.content
|
97
|
+
parsed_device_data = JSON.parse(device_data)
|
85
98
|
|
86
99
|
devices = []
|
87
|
-
|
100
|
+
parsed_device_data['devices'].each do |row|
|
88
101
|
device = Device.new
|
89
|
-
device.name = row
|
90
|
-
device.udid = row
|
102
|
+
device.name = row['name']
|
103
|
+
device.udid = row['deviceNumber'] # Apple doesn't provide the UDID on this page anymore
|
91
104
|
devices << device
|
92
105
|
end
|
93
106
|
|
94
|
-
if message = page.parser.at_xpath('//p[@class="devicesannounce"]/strong/text()').to_s.strip rescue nil
|
95
|
-
number_of_devices_available = message.scan(/\d{1,3}/).first.to_i
|
96
|
-
number_of_devices_available.times do
|
97
|
-
devices << nil
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
107
|
devices
|
102
108
|
end
|
103
109
|
|
104
110
|
def add_devices(*devices)
|
105
111
|
return if devices.empty?
|
106
112
|
|
107
|
-
get(
|
113
|
+
get('https://developer.apple.com/account/ios/device/deviceCreate.action')
|
108
114
|
|
109
115
|
begin
|
110
|
-
file = Tempfile.new(
|
111
|
-
file.write("
|
116
|
+
file = Tempfile.new(%w(devices .txt))
|
117
|
+
file.write("Device ID\tDevice Name")
|
112
118
|
devices.each do |device|
|
113
119
|
file.write("\n#{device.udid}\t#{device.name}")
|
114
120
|
end
|
115
121
|
file.rewind
|
116
122
|
|
117
|
-
|
118
|
-
|
119
|
-
|
123
|
+
form = page.form_with(:name => 'deviceImport') or raise UnexpectedContentError
|
124
|
+
|
125
|
+
upload = form.file_uploads.first
|
126
|
+
upload.file_name = file.path
|
127
|
+
form.radiobuttons.first.check()
|
128
|
+
form.submit
|
129
|
+
|
130
|
+
if form = page.form_with(:name => 'deviceSubmit')
|
131
|
+
form.method = 'POST'
|
132
|
+
form.field_with(:name => 'deviceNames').name = 'name'
|
133
|
+
form.field_with(:name => 'deviceNumbers').name = 'deviceNumber'
|
120
134
|
form.submit
|
135
|
+
elsif form = page.form_with(:name => 'deviceImport')
|
136
|
+
form.submit
|
137
|
+
else
|
138
|
+
raise UnexpectedContentError
|
121
139
|
end
|
140
|
+
|
122
141
|
ensure
|
123
142
|
file.close!
|
124
143
|
end
|
@@ -127,22 +146,38 @@ module Cupertino
|
|
127
146
|
def list_profiles(type = :development)
|
128
147
|
url = case type
|
129
148
|
when :development
|
130
|
-
|
149
|
+
'https://developer.apple.com/account/ios/profile/profileList.action?type=limited'
|
131
150
|
when :distribution
|
132
|
-
|
151
|
+
'https://developer.apple.com/account/ios/profile/profileList.action?type=production'
|
133
152
|
else
|
134
|
-
raise ArgumentError,
|
153
|
+
raise ArgumentError, 'Provisioning profile type must be :development or :distribution'
|
135
154
|
end
|
136
155
|
|
137
156
|
get(url)
|
138
157
|
|
158
|
+
regex = /profileDataURL = "([^"]*)"/
|
159
|
+
profile_data_url = (page.body.match regex or raise UnexpectedContentError)[1]
|
160
|
+
|
161
|
+
profile_data_url += case type
|
162
|
+
when :development
|
163
|
+
'&type=limited'
|
164
|
+
when :distribution
|
165
|
+
'&type=production'
|
166
|
+
end
|
167
|
+
|
168
|
+
get(profile_data_url)
|
169
|
+
profile_data = page.content
|
170
|
+
parsed_profile_data = JSON.parse(profile_data)
|
171
|
+
|
139
172
|
profiles = []
|
140
|
-
|
173
|
+
parsed_profile_data['provisioningProfiles'].each do |row|
|
141
174
|
profile = ProvisioningProfile.new
|
142
|
-
profile.name = row
|
175
|
+
profile.name = row['name']
|
143
176
|
profile.type = type
|
144
|
-
profile.app_id = row
|
145
|
-
profile.status = row
|
177
|
+
profile.app_id = row['appId']['appIdId']
|
178
|
+
profile.status = row['status']
|
179
|
+
profile.download_url = "https://developer.apple.com/account/ios/profile/profileContentDownload.action?displayId=#{row['provisioningProfileId']}"
|
180
|
+
profile.edit_url = "https://developer.apple.com/account/ios/profile/profileEdit.action?provisioningProfileId=#{row['provisioningProfileId']}"
|
146
181
|
profiles << profile
|
147
182
|
end
|
148
183
|
profiles
|
@@ -151,20 +186,10 @@ module Cupertino
|
|
151
186
|
def download_profile(profile)
|
152
187
|
list_profiles(profile.type)
|
153
188
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
download_url = row.at_xpath('td[@class="action"]/a')['href'].to_s.strip rescue nil
|
159
|
-
|
160
|
-
self.pluggable_parser.default = Mechanize::Download
|
161
|
-
download = get(download_url)
|
162
|
-
download.save
|
163
|
-
return download.filename
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
return nil
|
189
|
+
self.pluggable_parser.default = Mechanize::Download
|
190
|
+
download = get(profile.download_url)
|
191
|
+
download.save
|
192
|
+
download.filename
|
168
193
|
end
|
169
194
|
|
170
195
|
def manage_devices_for_profile(profile)
|
@@ -172,154 +197,74 @@ module Cupertino
|
|
172
197
|
|
173
198
|
list_profiles(profile.type)
|
174
199
|
|
175
|
-
|
176
|
-
page.parser.xpath('//fieldset[@id="fs-0"]/table/tbody/tr').each do |row|
|
177
|
-
break if modify_url
|
178
|
-
|
179
|
-
name = row.at_xpath('td[@class="profile"]//text()').to_s.strip rescue nil
|
180
|
-
|
181
|
-
if name == profile.name
|
182
|
-
row.css('td.action a').each do |a|
|
183
|
-
if /edit\.action/ === a['href']
|
184
|
-
modify_url = a['href']
|
185
|
-
end
|
186
|
-
end
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
|
-
if modify_url
|
191
|
-
get(modify_url)
|
200
|
+
get(profile.edit_url)
|
192
201
|
|
193
|
-
|
194
|
-
|
195
|
-
|
202
|
+
on, off = [], []
|
203
|
+
page.search('dd.selectDevices div.rows div').each do |row|
|
204
|
+
checkbox = row.search('input[type="checkbox"]').first
|
196
205
|
|
197
|
-
|
198
|
-
|
199
|
-
|
206
|
+
device = Device.new
|
207
|
+
device.name = row.search('span.title').text rescue nil
|
208
|
+
device.udid = checkbox['value'] rescue nil
|
200
209
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
end
|
210
|
+
if checkbox['checked']
|
211
|
+
on << device
|
212
|
+
else
|
213
|
+
off << device
|
206
214
|
end
|
215
|
+
end
|
207
216
|
|
208
|
-
|
217
|
+
devices = yield on, off
|
209
218
|
|
210
|
-
|
211
|
-
|
219
|
+
form = page.form_with(:name => 'profileEdit') or raise UnexpectedContentError
|
220
|
+
form.checkboxes_with(:name => 'deviceIds').each do |checkbox|
|
221
|
+
checkbox.check
|
222
|
+
if devices.detect{|device| device.udid == checkbox['value']}
|
212
223
|
checkbox.check
|
213
|
-
|
214
|
-
|
215
|
-
else
|
216
|
-
checkbox.uncheck
|
217
|
-
end
|
224
|
+
else
|
225
|
+
checkbox.uncheck
|
218
226
|
end
|
219
|
-
|
220
|
-
button = form.button_with(:name => 'submit')
|
221
|
-
form.click_button(button)
|
222
227
|
end
|
223
228
|
|
224
|
-
|
229
|
+
form.method = 'POST'
|
230
|
+
form.submit
|
225
231
|
end
|
226
232
|
|
227
233
|
def list_app_ids
|
228
|
-
get(
|
234
|
+
get('https://developer.apple.com/account/ios/identifiers/bundle/bundleList.action')
|
235
|
+
|
236
|
+
regex = /bundleDataURL = "([^"]*)"/
|
237
|
+
bundle_data_url = (page.body.match regex or raise UnexpectedContentError)[1]
|
238
|
+
|
239
|
+
get(bundle_data_url)
|
240
|
+
bundle_data = page.content
|
241
|
+
parsed_bundle_data = JSON.parse(bundle_data)
|
229
242
|
|
230
243
|
app_ids = []
|
231
|
-
|
244
|
+
parsed_bundle_data['appIds'].each do |row|
|
232
245
|
app_id = AppID.new
|
233
|
-
app_id.bundle_seed_id = row.
|
234
|
-
app_id.description = row
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
246
|
+
app_id.bundle_seed_id = [row['prefix'], row['identifier']].join(".")
|
247
|
+
app_id.description = row['name']
|
248
|
+
|
249
|
+
app_id.development_properties, app_id.distribution_properties = [], []
|
250
|
+
row['features'].each do |feature, value|
|
251
|
+
if value == true
|
252
|
+
app_id.development_properties << feature
|
253
|
+
elsif value.kind_of?(String) && !value.empty?
|
254
|
+
app_id.development_properties << "#{feature}: #{value}"
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
row['enabledFeatures'].each do |feature|
|
259
|
+
app_id.distribution_properties << feature
|
241
260
|
end
|
242
261
|
|
243
262
|
app_ids << app_id
|
244
263
|
end
|
264
|
+
|
245
265
|
app_ids
|
246
266
|
end
|
247
|
-
|
248
|
-
def list_pass_type_ids
|
249
|
-
get("https://developer.apple.com/ios/manage/passtypeids/index.action")
|
250
|
-
|
251
|
-
pass_type_ids = []
|
252
|
-
page.parser.xpath('//fieldset[@id="fs-0"]/table/tbody/tr').each do |row|
|
253
|
-
pass_type_id = PassTypeID.new
|
254
|
-
pass_type_id.card_id = row.at_xpath('td[@class="checkbox"]/input[@name="selectedValues"]')['value'].to_s.strip rescue nil
|
255
|
-
pass_type_id.id = row.at_xpath('td[@class="name"]/strong/text()').to_s.strip rescue nil
|
256
|
-
pass_type_id.description = row.at_xpath('td[@class="name"]/text()').to_s.strip rescue nil
|
257
|
-
pass_type_id.pass_certificates = row.at_xpath('td[@class="profile"]').inner_text.strip rescue nil
|
258
|
-
|
259
|
-
pass_type_ids << pass_type_id
|
260
|
-
end
|
261
|
-
pass_type_ids
|
262
|
-
end
|
263
|
-
|
264
|
-
def list_pass_certificates(pass_type_id)
|
265
|
-
pass_type_id = list_pass_type_ids().delete_if{ |item| item.id != pass_type_id }.shift rescue nil
|
266
|
-
return [] if pass_type_id.nil?
|
267
|
-
|
268
|
-
get("https://developer.apple.com/ios/manage/passtypeids/configure.action?displayId=#{pass_type_id.card_id}")
|
269
|
-
|
270
|
-
pass_certificates = []
|
271
|
-
page.parser.xpath('//form[@name="form_logginMemberCert"]/table/tr[position()>1]').each do |row|
|
272
|
-
pass_certificate = PassCertificate.new
|
273
|
-
pass_certificate.name = row.at_xpath('td[1]').inner_text.strip rescue nil
|
274
|
-
pass_certificate.status = row.at_xpath('td[2]/span/text()').to_s.strip rescue nil
|
275
|
-
pass_certificate.expiration_date = row.at_xpath('td[3]/text()').to_s.strip rescue nil
|
276
|
-
pass_certificate.certificate_id = row.at_xpath('td[4]//a[@id="form_logginMemberCert_"]')['href'].to_s.strip.match(/certDisplayId=(.+?)$/)[1] rescue nil
|
277
|
-
|
278
|
-
pass_certificates << pass_certificate unless pass_certificate.certificate_id.nil?
|
279
|
-
end
|
280
|
-
pass_certificates
|
281
|
-
end
|
282
|
-
|
283
|
-
def add_pass_type_id(pass_type_id, description)
|
284
|
-
get("https://developer.apple.com/ios/manage/passtypeids/add.action")
|
285
|
-
|
286
|
-
if form = page.form_with(:name => 'save')
|
287
|
-
form['cardName'] = description
|
288
|
-
form['cardIdentifier'] = pass_type_id
|
289
|
-
|
290
|
-
button = form.button_with(:name => 'submit')
|
291
|
-
form.click_button(button)
|
292
|
-
end
|
293
|
-
end
|
294
|
-
|
295
|
-
def add_pass_certificate(pass_type_id, csr_path, aps_cert_type = 'development')
|
296
|
-
pass_type_id = list_pass_type_ids().delete_if{ |item| item.id != pass_type_id }.shift rescue nil
|
297
|
-
return if pass_type_id.nil?
|
298
|
-
|
299
|
-
csr_contents = ::File.open(csr_path, "rb").read
|
300
|
-
|
301
|
-
post("https://developer.apple.com/ios/assistant/passtypecommit.action", { 'cardIdValue' => pass_type_id.card_id, 'csrValue' => csr_contents, 'apsCertType' => aps_cert_type })
|
302
|
-
|
303
|
-
JSON.parse(page.content)
|
304
|
-
end
|
305
|
-
|
306
|
-
def pass_type_generate(aps_cert_type = 'development')
|
307
|
-
post("https://developer.apple.com/ios/assistant/passtypegenerate.action", { 'apsCertType' => aps_cert_type })
|
308
|
-
|
309
|
-
::JSON.parse(page.content)
|
310
|
-
end
|
311
|
-
|
312
|
-
def download_pass_certificate(pass_type_id, certificate_id = nil)
|
313
|
-
pass_certificate = (certificate_id.nil? ? list_pass_certificates(pass_type_id).last : list_pass_certificates(pass_type_id).delete_if{ |item| item.certificate_id != certificate_id }.shift) rescue nil
|
314
|
-
return nil if pass_certificate.nil?
|
315
267
|
|
316
|
-
self.pluggable_parser.default = Mechanize::Download
|
317
|
-
download = get("/ios/manage/passtypeids/downloadCert.action?certDisplayId=#{pass_certificate.certificate_id}")
|
318
|
-
download.filename = "#{pass_certificate.certificate_id}.cer"
|
319
|
-
download.save
|
320
|
-
return download.filename
|
321
|
-
end
|
322
|
-
|
323
268
|
private
|
324
269
|
|
325
270
|
def login!
|
@@ -332,9 +277,9 @@ module Cupertino
|
|
332
277
|
|
333
278
|
def select_team!
|
334
279
|
if form = page.form_with(:name => 'saveTeamSelection')
|
335
|
-
|
336
|
-
team_option =
|
337
|
-
team_option.
|
280
|
+
# self.team now stores team ID, not name
|
281
|
+
team_option = form.radiobutton_with(:value => self.team)
|
282
|
+
team_option.check
|
338
283
|
|
339
284
|
button = form.button_with(:name => 'action:saveTeamSelection!save')
|
340
285
|
form.click_button(button)
|
@@ -10,17 +10,17 @@ command :'certificates:list' do |c|
|
|
10
10
|
say_warning "No #{type} certificates found." and abort if certificates.empty?
|
11
11
|
|
12
12
|
table = Terminal::Table.new do |t|
|
13
|
-
t << ["Name", "
|
13
|
+
t << ["Name", "Type", "Expiration Date", "Status"]
|
14
14
|
t.add_separator
|
15
15
|
certificates.each do |certificate|
|
16
16
|
status = case certificate.status
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
when "Issued"
|
18
|
+
certificate.status.green
|
19
|
+
else
|
20
|
+
certificate.status.red
|
21
21
|
end
|
22
22
|
|
23
|
-
t << [certificate.name, certificate.
|
23
|
+
t << [certificate.name, certificate.type, certificate.expiration_date, status]
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
@@ -36,7 +36,7 @@ command :'devices:add' do |c|
|
|
36
36
|
|
37
37
|
devices = []
|
38
38
|
args.each do |arg|
|
39
|
-
components = arg.strip.gsub(
|
39
|
+
components = arg.strip.gsub(/"/, '').split(/\=/)
|
40
40
|
device = Device.new
|
41
41
|
device.name = components.first
|
42
42
|
device.udid = components.last
|
@@ -4,12 +4,12 @@ command :login do |c|
|
|
4
4
|
c.description = ''
|
5
5
|
|
6
6
|
c.action do |args, options|
|
7
|
-
say_warning "You are already authenticated" if Security::InternetPassword.find(:server => Cupertino::
|
7
|
+
say_warning "You are already authenticated" if Security::InternetPassword.find(:server => Cupertino::ProvisioningPortal::HOST)
|
8
8
|
|
9
9
|
user = ask "Username:"
|
10
10
|
pass = password "Password:"
|
11
11
|
|
12
|
-
Security::InternetPassword.add(Cupertino::
|
12
|
+
Security::InternetPassword.add(Cupertino::ProvisioningPortal::HOST, user, pass)
|
13
13
|
|
14
14
|
say_ok "Account credentials saved"
|
15
15
|
end
|
@@ -4,9 +4,9 @@ command :logout do |c|
|
|
4
4
|
c.description = ''
|
5
5
|
|
6
6
|
c.action do |args, options|
|
7
|
-
say_error "You are not authenticated" and abort unless Security::InternetPassword.find(:server => Cupertino::
|
7
|
+
say_error "You are not authenticated" and abort unless Security::InternetPassword.find(:server => Cupertino::ProvisioningPortal::HOST)
|
8
8
|
|
9
|
-
Security::InternetPassword.delete(:server => Cupertino::
|
9
|
+
Security::InternetPassword.delete(:server => Cupertino::ProvisioningPortal::HOST)
|
10
10
|
|
11
11
|
say_ok "Account credentials removed"
|
12
12
|
end
|
@@ -47,11 +47,11 @@ command :'profiles:manage:devices' do |c|
|
|
47
47
|
lines = ["# Comment / Uncomment Devices to Turn Off / On for Provisioning Profile"]
|
48
48
|
lines += on.collect{|device| "#{device}"}
|
49
49
|
lines += off.collect{|device| "# #{device}"}
|
50
|
-
result = ask_editor lines.join("\n")
|
50
|
+
(result = ask_editor lines.join("\n")) or abort("EDITOR undefined. Try run 'export EDITOR=vi'")
|
51
51
|
|
52
52
|
devices = []
|
53
53
|
result.split(/\n+/).each do |line|
|
54
|
-
next if
|
54
|
+
next if /^#/ === line
|
55
55
|
components = line.split(/\s+/)
|
56
56
|
device = Device.new
|
57
57
|
device.udid = components.pop
|
@@ -5,6 +5,5 @@ require 'cupertino/provisioning_portal/commands/certificates'
|
|
5
5
|
require 'cupertino/provisioning_portal/commands/devices'
|
6
6
|
require 'cupertino/provisioning_portal/commands/profiles'
|
7
7
|
require 'cupertino/provisioning_portal/commands/app_ids'
|
8
|
-
require 'cupertino/provisioning_portal/commands/pass_type_ids'
|
9
8
|
require 'cupertino/provisioning_portal/commands/login'
|
10
9
|
require 'cupertino/provisioning_portal/commands/logout'
|
@@ -24,19 +24,28 @@ module Cupertino
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def team
|
27
|
-
|
28
|
-
|
27
|
+
# we're working with radio buttons instead of a drop down menu
|
28
|
+
teams = page.form_with(:name => 'saveTeamSelection').radiobuttons
|
29
|
+
# create a dictionary of team.value -> team name
|
30
|
+
formatted_teams = {}
|
31
|
+
teams.each do |team|
|
32
|
+
# we can't use team.label as this only returns the last label
|
33
|
+
# Apple use two labels with the same for="", we want the first
|
34
|
+
formatted_teams[team.value] = page.search("label[for=\"#{team.dom_id}\"]").first.text.strip
|
35
|
+
end
|
36
|
+
teamname = choose "Select a team:", *formatted_teams.values
|
37
|
+
@team ||= formatted_teams.key(teamname)
|
29
38
|
end
|
30
39
|
end
|
31
40
|
end
|
32
41
|
|
33
42
|
@agent
|
34
43
|
end
|
35
|
-
|
44
|
+
|
36
45
|
def pluralize(n, singular, plural = nil)
|
37
46
|
n.to_i == 1 ? "1 #{singular}" : "#{n} #{plural || singular + 's'}"
|
38
47
|
end
|
39
|
-
|
48
|
+
|
40
49
|
def try
|
41
50
|
return unless block_given?
|
42
51
|
|
@@ -3,7 +3,10 @@ require 'certified'
|
|
3
3
|
|
4
4
|
module Cupertino
|
5
5
|
module ProvisioningPortal
|
6
|
+
HOST = "developer.apple.com"
|
7
|
+
|
6
8
|
class UnsuccessfulAuthenticationError < RuntimeError; end
|
9
|
+
class UnexpectedContentError < RuntimeError; end
|
7
10
|
|
8
11
|
class Device < Struct.new(:name, :udid)
|
9
12
|
def to_s
|
@@ -11,7 +14,7 @@ module Cupertino
|
|
11
14
|
end
|
12
15
|
end
|
13
16
|
|
14
|
-
class Certificate < Struct.new(:name, :type, :
|
17
|
+
class Certificate < Struct.new(:name, :type, :expiration_date, :status, :download_url) #:provisioning_profiles,
|
15
18
|
def to_s
|
16
19
|
"#{self.name}"
|
17
20
|
end
|
@@ -23,18 +26,18 @@ module Cupertino
|
|
23
26
|
end
|
24
27
|
end
|
25
28
|
|
26
|
-
class ProvisioningProfile < Struct.new(:name, :type, :app_id, :status)
|
29
|
+
class ProvisioningProfile < Struct.new(:name, :type, :app_id, :status, :download_url, :edit_url)
|
27
30
|
def to_s
|
28
31
|
"#{self.name}"
|
29
32
|
end
|
30
33
|
end
|
31
|
-
|
34
|
+
|
32
35
|
class PassTypeID < Struct.new(:description, :id, :pass_certificates, :card_id)
|
33
36
|
def to_s
|
34
37
|
"#{self.id} #{self.description}"
|
35
38
|
end
|
36
39
|
end
|
37
|
-
|
40
|
+
|
38
41
|
class PassCertificate < Struct.new(:name, :status, :expiration_date, :certificate_id)
|
39
42
|
def to_s
|
40
43
|
"#{self.certificate_id}"
|
data/lib/cupertino.rb
CHANGED