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.
@@ -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::HOSTNAME)
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::HOSTNAME}", uri) unless /^https?/ === uri
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
- when %r{Sign in with your Apple ID}
28
- login! and redo
29
- when %r{Select Your Team}
30
- select_team! and redo
31
- else
32
- return page
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
- when :development
42
- "https://developer.apple.com/ios/manage/certificates/team/index.action"
43
- when :distribution
44
- "https://developer.apple.com/ios/manage/certificates/team/distribute.action"
45
- else
46
- raise ArgumentError, "Certificate type must be :development or :distribution"
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
- page.parser.xpath('//div[@class="nt_multi"]/table/tbody/tr').each do |row|
67
+ parsed_certificate_data['certRequests'].each do |row|
53
68
  certificate = Certificate.new
54
- certificate.name = row.at_xpath('td[@class="name"]//p/text()').to_s.strip rescue nil
69
+ certificate.name = row['name']
55
70
  certificate.type = type
56
- certificate.provisioning_profiles = row.at_xpath('td[@class="profiles"]/text()').to_s.strip.split(/\n+/) rescue []
57
- certificate.expiration_date = row.at_xpath('td[@class="date"]/text()').to_s.strip rescue nil
58
- certificate.status = row.at_xpath('td[@class="status"]/text()').to_s.strip rescue nil
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
- page.parser.xpath('//div[@class="nt_multi"]/table/tbody/tr').each do |row|
68
- name = row.at_xpath('td[@class="name"]//p/text()').to_s.strip rescue nil
69
-
70
- if name == certificate.name
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("https://developer.apple.com/ios/manage/devices/index.action")
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
- page.parser.xpath('//fieldset[@id="fs-0"]/table/tbody/tr').each do |row|
100
+ parsed_device_data['devices'].each do |row|
88
101
  device = Device.new
89
- device.name = row.at_xpath('td[@class="name"]/span/text()').to_s.strip rescue nil
90
- device.udid = row.at_xpath('td[@class="id"]/text()').to_s.strip rescue nil
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("https://developer.apple.com/ios/manage/devices/upload.action")
113
+ get('https://developer.apple.com/account/ios/device/deviceCreate.action')
108
114
 
109
115
  begin
110
- file = Tempfile.new(['devices', '.txt'])
111
- file.write("deviceIdentifier\tdeviceName")
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
- if form = page.form_with(:name => 'saveupload')
118
- upload = form.file_uploads.first
119
- upload.file_name = file.path
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
- "https://developer.apple.com/ios/manage/provisioningprofiles/index.action"
149
+ 'https://developer.apple.com/account/ios/profile/profileList.action?type=limited'
131
150
  when :distribution
132
- "https://developer.apple.com/ios/manage/provisioningprofiles/viewDistributionProfiles.action"
151
+ 'https://developer.apple.com/account/ios/profile/profileList.action?type=production'
133
152
  else
134
- raise ArgumentError, "Provisioning profile type must be :development or :distribution"
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
- page.parser.xpath('//fieldset[@id="fs-0"]/table/tbody/tr').each do |row|
173
+ parsed_profile_data['provisioningProfiles'].each do |row|
141
174
  profile = ProvisioningProfile.new
142
- profile.name = row.at_xpath('td[@class="profile"]//text()').to_s.strip rescue nil
175
+ profile.name = row['name']
143
176
  profile.type = type
144
- profile.app_id = row.at_xpath('td[@class="appid"]/text()').to_s.strip rescue nil
145
- profile.status = row.at_xpath('td[@class="statusXcode"]/text()').to_s.strip rescue nil
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
- page.parser.xpath('//fieldset[@id="fs-0"]/table/tbody/tr').each do |row|
155
- name = row.at_xpath('td[@class="profile"]//span/text()').to_s.strip rescue nil
156
-
157
- if name == profile.name
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
- modify_url = nil
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
- on, off = [], []
194
- page.parser.css('.checkboxlist.last td').each do |td|
195
- checkbox = td.at_xpath('input[@type="checkbox"]')
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
- device = Device.new
198
- device.name = td.at_xpath('label/text()').to_s.strip rescue nil
199
- device.udid = checkbox['value'].to_s.strip rescue nil
206
+ device = Device.new
207
+ device.name = row.search('span.title').text rescue nil
208
+ device.udid = checkbox['value'] rescue nil
200
209
 
201
- if checkbox['checked']
202
- on << device
203
- else
204
- off << device
205
- end
210
+ if checkbox['checked']
211
+ on << device
212
+ else
213
+ off << device
206
214
  end
215
+ end
207
216
 
208
- devices = yield on, off
217
+ devices = yield on, off
209
218
 
210
- form = page.form_with(:name => 'save')
211
- form.checkboxes_with(:name => "selectedDevices").each do |checkbox|
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
- if devices.detect{|device| device.udid == checkbox['value']}
214
- checkbox.check
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
- return nil
229
+ form.method = 'POST'
230
+ form.submit
225
231
  end
226
232
 
227
233
  def list_app_ids
228
- get("https://developer.apple.com/ios/manage/bundles/index.action")
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
- page.parser.xpath('//div[@class="nt_multi"]/table/tbody/tr').each do |row|
244
+ parsed_bundle_data['appIds'].each do |row|
232
245
  app_id = AppID.new
233
- app_id.bundle_seed_id = row.at_xpath('td[@class="name"]/strong/text()').to_s.strip rescue nil
234
- app_id.description = row.at_xpath('td[@class="name"]/text()').to_s.strip rescue nil
235
-
236
- keys = row.xpath('td[@class="name"]/p/text()').collect(&:to_s).collect(&:strip)
237
- app_id.development_properties, app_id.distribution_properties = row.xpath('td')[1..2].collect do |td|
238
- values = td.xpath('p//text()').collect(&:to_s).collect(&:strip).reject{|text| text.empty?}
239
- keys.zip(values)
240
- keys.zip(values)
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
- team_list = form.field_with(:name => 'memberDisplayId')
336
- team_option = team_list.option_with(:text => self.team)
337
- team_option.select
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", "Provisioning Profiles", "Expiration Date", "Status"]
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
- when "Issued"
18
- certificate.status.green
19
- else
20
- certificate.status.red
17
+ when "Issued"
18
+ certificate.status.green
19
+ else
20
+ certificate.status.red
21
21
  end
22
22
 
23
- t << [certificate.name, certificate.provisioning_profiles.join("\n"), certificate.expiration_date, status]
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(/\"/, '').split(/\=/)
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::HOSTNAME)
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::HOSTNAME, user, pass)
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::HOSTNAME)
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::HOSTNAME)
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 /^\#/ === line
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
- teams = page.form_with(:name => 'saveTeamSelection').field_with(:name => 'memberDisplayId').options.collect(&:text)
28
- @team ||= choose "Select a team:", *teams
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, :provisioning_profiles, :expiration_date, :status)
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
@@ -1,4 +1,3 @@
1
1
  module Cupertino
2
- VERSION = '0.7.1'
3
- HOSTNAME = "developer.apple.com"
2
+ VERSION = '0.8.0'
4
3
  end