appsendr 0.0.3 → 0.0.5

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.
@@ -0,0 +1,375 @@
1
+ module AppSendr::Command
2
+ class Portal < Base
3
+
4
+ attr_accessor :credentials
5
+
6
+ require 'rubygems'
7
+ require 'mechanize'
8
+
9
+ @@agent = Mechanize.new
10
+ @@agent.user_agent_alias = 'Mac FireFox'
11
+ @@agent.follow_meta_refresh = true
12
+
13
+ @@available_devices = []
14
+ @@available_profiles = []
15
+
16
+ LOGIN_URL = 'https://developer.apple.com/ios/manage/overview/index.action'
17
+ DEVICES_URL = "https://developer.apple.com/ios/manage/devices/index.action"
18
+ DEVICE_BULK_UPLOAD_URL = "http://developer.apple.com/ios/manage/devices/saveupload.action"
19
+ SAVE_TEAM_URL = "http://developer.apple.com/membercenter/saveTeamSelection.action"
20
+
21
+ DEV_PROVISIONING_PROFILE_URL = "https://developer.apple.com/ios/manage/provisioningprofiles/index.action"
22
+ DIST_PROVISIONING_PROFILE_URL = "https://developer.apple.com/ios/manage/provisioningprofiles/viewDistributionProfiles.action"
23
+
24
+ CREATE_ADHOC_PROFILE_URL = "http://developer.apple.com/ios/manage/provisioningprofiles/create.action?type=2"
25
+ DOWNLOAD_PROFILE_URL_WO_ID ="/ios/manage/provisioningprofiles/download.action?blobId="
26
+ EDIT_PROFILE_URL_WO_ID = "/ios/manage/provisioningprofiles/edit.action?provDisplayId="
27
+
28
+
29
+ def index
30
+
31
+ result = check_for_manual_login
32
+
33
+ if result
34
+ @available_devices = get_devices
35
+ @available_dev_profiles = development_provisioning_profiles
36
+ @available_dist_profiles = distribution_provisioning_profiles
37
+
38
+ display "#{@available_devices.length} Devices"
39
+ display "#{@available_dev_profiles.length} Development Profiles"
40
+ display "#{@available_dist_profiles.length} Distribution Profiles"
41
+
42
+ end
43
+ end
44
+
45
+ def devices
46
+ result = check_for_manual_login
47
+
48
+ if result
49
+ @available_devices = get_devices
50
+ @available_devices.each{|device|
51
+ display "#{device[:name]} - #{device[:id]}"
52
+ }
53
+ end
54
+ end
55
+
56
+ def profiles
57
+ result = check_for_manual_login
58
+
59
+ if result
60
+ @available_dev_profiles = development_provisioning_profiles
61
+ @available_dist_profiles = distribution_provisioning_profiles
62
+
63
+ display "\nDevelopment Profiles\n"
64
+ @available_dev_profiles.each{|profile|
65
+
66
+ display "#{profile[:name]} - #{profile[:app_id]}"
67
+ }
68
+ display "\nDistribution Profiles\n"
69
+ @available_dist_profiles.each{|profile|
70
+ display "#{profile[:name]} - #{profile[:app_id]}"
71
+ }
72
+
73
+
74
+ end
75
+ end
76
+
77
+ def update
78
+
79
+ @remote_devices = appsendr.devices(nil,false)
80
+ @available_devices = get_devices
81
+
82
+ ## get devices for users for this app
83
+ ## get all devices from portal
84
+ ## reconcile
85
+ ## add device not on portal
86
+ ## get profile for app
87
+ ##
88
+ end
89
+
90
+
91
+ private
92
+ def check_for_manual_login
93
+ username = extract_option('--username', false)
94
+ password = extract_option('--password', false)
95
+
96
+ result = false
97
+ if username and password
98
+ result = login(username,password)
99
+
100
+ error "\nAuthentication failed" unless result
101
+
102
+ else
103
+ result = login(user,pass)
104
+ end
105
+
106
+ return result
107
+ end
108
+
109
+
110
+ ###
111
+
112
+
113
+ def login(username, password)
114
+
115
+ # Get login page
116
+ #puts "- fetching login page"
117
+ #page = agent.get('https://phobos.apple.com/WebObjects/MZLabel.woa/wa/default')
118
+ page = @@agent.get(LOGIN_URL)
119
+
120
+
121
+ # Submit form to login
122
+ login_form = page.forms.first
123
+ login_form['theAccountName']=username
124
+ login_form['theAccountPW']=password
125
+ page = @@agent.submit(login_form)
126
+
127
+ # Fail if it looks like we ended up back at a login page
128
+ if page.form('appleConnectForm')
129
+ #puts "Error: Login failed"
130
+ return nil
131
+ #exit
132
+ else
133
+ select_form = page.form('saveTeamSelection')
134
+ if select_form
135
+ options = []
136
+ select_form.fields.first.options.each{|option|
137
+ display "#{options.length + 1}. #{option.text}"
138
+ options.push([option.text,option.value])
139
+
140
+ }
141
+ print "Enter the # for the account you wish to login to: "
142
+ selection = ask
143
+ if selection.to_i > (options.length )
144
+ error "Invalid selection"
145
+ return nil
146
+
147
+ end
148
+
149
+ team = options[selection.to_i - 1]
150
+ select_form['memberDisplayId'] = team[1]
151
+ page = @@agent.submit(select_form, select_form.buttons[1])
152
+ return page
153
+ else
154
+ return page
155
+ end
156
+ end
157
+ end
158
+
159
+ def get_details(username, password)
160
+ if login(username,password)
161
+ @available_devices = get_devices
162
+ @available_dev_profiles = development_provisioning_profiles
163
+ @available_dist_profiles = distribution_provisioning_profiles
164
+ display ""
165
+
166
+
167
+ end
168
+ end
169
+
170
+
171
+ #####
172
+ # provisioning
173
+ def get_devices
174
+ page = @@agent.get(DEVICES_URL)
175
+ table = page.at("//table/tbody") # This passes through to hpricot
176
+ devices = []
177
+ return devices unless table
178
+ table.children.each{|tr|
179
+ children = tr.children
180
+ device_name = children[2].content.strip
181
+ device_id = children[4].content.strip
182
+ devices.push({:name => device_name, :id => device_id })
183
+ }
184
+
185
+ return devices;
186
+
187
+ end
188
+
189
+ def upload_bulk_file
190
+ temp_file = TempFile.new('bulk_upload')
191
+ temp_file.write(device_file)
192
+ temp_file.close
193
+ @@agent.post(DEVICE_BULK_UPLOAD_URL, {:upload => temp_file})
194
+ end
195
+
196
+
197
+
198
+ #####
199
+ # provisioning
200
+
201
+ def provisioning_profiles(url)
202
+ page = @@agent.get(url)
203
+ table = page.at("//table/tbody")
204
+ profiles = []
205
+ return profiles unless table
206
+
207
+ table.children.each{|tr|
208
+ children = tr.children
209
+ cert_key = children[0].children[0]['value'].strip
210
+ cert_name = children[2].content.strip
211
+ app_id = children[4].content.strip
212
+ profiles.push({:name => cert_name, :id => cert_key, :app_id => app_id })
213
+ #p children
214
+ }
215
+ return profiles
216
+ end
217
+
218
+ def development_provisioning_profiles
219
+ provisioning_profiles(DEV_PROVISIONING_PROFILE_URL)
220
+ end
221
+
222
+ def distribution_provisioning_profiles
223
+
224
+ provisioning_profiles(DIST_PROVISIONING_PROFILE_URL)
225
+
226
+ end
227
+
228
+
229
+ def download_provisioning_profile(id)
230
+ page = @@agent.get(DOWNLOAD_PROFILE_URL_WO_ID+id)
231
+ return page
232
+ end
233
+
234
+ def edit_provisioning_profile(id)
235
+ page = @@agent.get(EDIT_PROFILE_URL_WO_ID+id)
236
+ end
237
+
238
+ def create_provisioning_profile
239
+ page = @@agent.get(CREATE_ADHOC_PROFILE_URL) #adhoc profiles
240
+ create_form = page.forms.first
241
+
242
+ end
243
+
244
+ ## credentials
245
+
246
+ def user # :nodoc:
247
+ get_credentials
248
+ @credentials[0]
249
+ end
250
+
251
+ def pass # :nodoc:
252
+ get_credentials
253
+ @credentials[1]
254
+ end
255
+
256
+
257
+ def reauthorize
258
+ user_pass = ask_for_credentials
259
+ @credentials = auth_credentials(user_pass[0],user_pass[1])
260
+ write_credentials
261
+ end
262
+
263
+ def auth_credentials(username,password)
264
+ login_agent = Mechanize.new
265
+
266
+ page = login_agent.get(LOGIN_URL)
267
+
268
+
269
+ # Submit form to login
270
+ login_form = page.forms.first
271
+ login_form['theAccountName']=username
272
+ login_form['theAccountPW']=password
273
+ page = login_agent.submit(login_form);
274
+ # Fail if it looks like we ended up back at a login page
275
+ if (!page.form('appleConnectForm').nil?)
276
+ return false
277
+ else
278
+ return [username,password]
279
+ end
280
+ end
281
+
282
+ def get_credentials # :nodoc:
283
+ return if @credentials
284
+ unless @credentials = read_credentials
285
+ user_pass = ask_for_credentials
286
+
287
+ @credentials = auth_credentials(user_pass[0],user_pass[1])
288
+ retry unless @credentials
289
+ save_credentials
290
+ end
291
+ @credentials
292
+ end
293
+
294
+ def read_credentials
295
+ File.exists?(credentials_file) and File.read(credentials_file).split("\n")
296
+ end
297
+
298
+ def echo_off
299
+ system "stty -echo"
300
+ end
301
+
302
+ def echo_on
303
+ system "stty echo"
304
+ end
305
+
306
+ def ask_for_credentials
307
+ puts "Enter your Apple Developer credentials. (These are never sent to AppSendr)"
308
+
309
+ print "Username: "
310
+ user = ask
311
+
312
+ print "Password: "
313
+ password = ask_for_password
314
+
315
+ [ user, password ]
316
+ end
317
+
318
+ def ask_for_password
319
+ echo_off
320
+ password = ask
321
+ puts
322
+ echo_on
323
+ return password
324
+ end
325
+
326
+ def save_credentials
327
+ begin
328
+ write_credentials
329
+ # if self.credentials
330
+ # delete_credentials
331
+ # raise e unless retry_login?
332
+ #
333
+ # display "\nAuthentication failed"
334
+ # user_pass = ask_for_credentials
335
+ # @credentials = auth_credentials(user_pass[0],user_pass[1])
336
+ # retry
337
+ rescue Exception => e
338
+ delete_credentials
339
+ end
340
+ end
341
+
342
+ def retry_login?
343
+ @login_attempts ||= 0
344
+ @login_attempts += 1
345
+ @login_attempts < 3
346
+ end
347
+
348
+ def write_credentials
349
+ FileUtils.mkdir_p(File.dirname(credentials_file))
350
+ File.open(credentials_file, 'w') do |f|
351
+ f.puts self.credentials
352
+ end
353
+ set_credentials_permissions
354
+ end
355
+
356
+ def set_credentials_permissions
357
+ FileUtils.chmod 0700, File.dirname(credentials_file)
358
+ FileUtils.chmod 0600, credentials_file
359
+ end
360
+
361
+ def delete_credentials
362
+ FileUtils.rm_f(credentials_file)
363
+ end
364
+
365
+ def credentials_file
366
+ "#{home_directory}/#{AppSendr::APPDROPPR_DIR}/portal_credentials"
367
+ end
368
+
369
+ def credentials_setup?
370
+ File.exist?(credentials_file)
371
+ end
372
+
373
+
374
+ end
375
+ end
@@ -33,16 +33,15 @@ module AppSendr::Command
33
33
  def add
34
34
 
35
35
  if require_project(2,"add testers","an email and name", true)
36
- f = testers_file_append
37
- return unless f
38
36
  email = args.shift.strip
39
37
  name = args.join(" ").strip
40
- f.puts "#{email},#{name}"
41
- f.close
38
+
42
39
  begin
43
40
  response = appsendr.add_tester(read_app_id,email,name)
41
+ message "#{email} added"
42
+
44
43
  rescue RestClient::RequestFailed => e
45
- display "=== Errors"
44
+ message "Errors"
46
45
  response = JSON.parse(e.http_body)
47
46
  response['message'].each{|err|
48
47
  display err.join(" ")
@@ -55,45 +54,29 @@ module AppSendr::Command
55
54
  def remove
56
55
  if require_project(1,"remove testers","an email")
57
56
  entered_email = args.shift.strip
58
-
59
- f = testers_file
60
- return unless f
61
- out = ""
62
- found_email = nil
63
- f.each do |line|
64
- tester = line.split(',')
65
- email = tester.first.strip
66
- if email == entered_email
67
- found_email = email
68
- out << ""
69
- else
70
- out << line
71
- end
72
- end
73
- f.pos = 0
74
- f.print out
75
- f.truncate(f.pos)
76
-
77
57
  appsendr.remove_tester(read_app_id,entered_email)
58
+ message "Removed tester"
78
59
 
79
60
  end
80
61
  end
81
62
 
82
63
  def clear
83
64
  if require_project(0,"clear testers",nil)
84
- f = testers_file_truncate
85
- return unless f
86
- f.close
87
-
65
+ message "Removing all testers"
88
66
  appsendr.add_tester(read_app_id)
89
67
 
90
68
  end
91
69
  end
92
70
 
93
71
  def notify
94
- require_project_dir("to notify testers")
95
- require_project_droppr
96
- appsendr.notify(read_app_id)
72
+ if require_project(0,"clear testers",nil)
73
+ require_project_droppr
74
+ message "Notifying testers"
75
+ group = args.join(" ").strip
76
+
77
+ appsendr.notify(read_app_id,group)
78
+ end
79
+
97
80
 
98
81
  end
99
82
 
@@ -1,5 +1,5 @@
1
1
  module AppSendr
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.5"
3
3
  PROJECT_DIR = ".appsendr"
4
4
  APPDROPPR_DIR = ".appsendr"
5
5
  end
@@ -44,13 +44,40 @@ module AppSendr
44
44
  File.exists?(project_appsendr_app) and File.read(project_appsendr_app).split("\n")
45
45
  end
46
46
 
47
+ def error(msg)
48
+ STDERR.puts(msg)
49
+ exit 1
50
+ end
51
+
52
+ def message(msg)
53
+ puts("=== #{msg}")
54
+ end
55
+
56
+ ### split
57
+ def is_file_to_large?(file)
58
+ in_mb = size_of_file(file).to_f
59
+ if in_mb > 10
60
+ return true
61
+ else
62
+ return false
63
+ end
64
+ end
65
+
66
+ def size_of_file(file)
67
+ size = File.size(file)
68
+ mb = 1024.0 * 1024.0
69
+ in_mb = size / mb
70
+
71
+ end
72
+
73
+ ## droppr
47
74
  def has_project_droppr?
48
75
  return false unless File.directory?(project_appsendr)
49
76
  return File.exist?(project_appsendr_app)
50
77
  end
51
78
 
52
79
  def require_project_droppr
53
- error "Project directory doesn't exist. Have you created an app droppr for this app?" unless has_project_droppr?
80
+ error "Project directory doesn't exist. Have you created an this app on appsendr?" unless has_project_droppr?
54
81
  end
55
82
 
56
83
  def require_project_dir(action)
@@ -98,16 +125,13 @@ module AppSendr
98
125
  else
99
126
  require_project_dir(action)
100
127
  error "This app is already linked to appsendr." unless !has_project_droppr?
101
-
102
128
  end
103
129
 
104
130
  return false
105
131
  end
106
-
107
- def error(msg)
108
- STDERR.puts(msg)
109
- exit 1
110
- end
132
+
133
+
134
+
111
135
  end
112
136
  end
113
137