appsendr 0.0.6 → 1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,52 +0,0 @@
1
- module AppSendr::Command
2
- class Collaborators < Build ## Inherhits from Deploy
3
-
4
- def index
5
- if in_project_dir?
6
- unless has_project_droppr?
7
- require_project_droppr
8
- return
9
- end
10
-
11
- testers = appsendr.collaborators(read_app_id)
12
- if testers["message"].size > 0
13
- message "Your collaborators"
14
- i = 0
15
- display testers["message"].map {|app, id|
16
- "#{i+=1}. #{app['name']} - #{app['email']}"
17
- }.join("\n")
18
- else
19
- display "You have no collaborators."
20
- end
21
- end
22
- end
23
-
24
-
25
- def add
26
-
27
- if require_project(2,"add collaborators","an email", true)
28
- email = args.shift.strip
29
-
30
- begin
31
- response = appsendr.add_collaborator(read_app_id,email)
32
- rescue RestClient::RequestFailed => e
33
- message "Errors"
34
- response = JSON.parse(e.http_body)
35
- response['message'].each{|err|
36
- display err.join(" ")
37
- }
38
- end
39
-
40
- end
41
- end
42
-
43
- def remove
44
- if require_project(1,"remove testers","an email")
45
- entered_email = args.shift.strip
46
-
47
- appsendr.remove_collaborator(read_app_id,entered_email)
48
-
49
- end
50
- end
51
- end
52
- end
@@ -1,36 +0,0 @@
1
- require 'optparse'
2
-
3
- module AppSendr::Command
4
- class Ipa < Build ## Inherhits from Deploy
5
- def create
6
-
7
- app_name = args.join(" ").strip
8
- @app_dir = Dir.pwd
9
- @app_path = @app_dir+"/#{app_name}"
10
-
11
- payload_path = @app_dir+"/Payload"
12
- ipa_path = @app_dir+"/#{ipa_app_name(app_name).delete('^A-Za-z0-9\.\_\-')}"
13
-
14
-
15
- FileUtils.rm_rf payload_path
16
- Dir.mkdir(payload_path)
17
- #display "#{@app_path.cl_escape} #{payload_path.cl_escape}/#{app_name.cl_escape}"
18
- `cp -r #{@app_path.cl_escape} #{payload_path.cl_escape+"/"+app_name.cl_escape}`
19
- build_ipa(payload_path.cl_escape,ipa_path.cl_escape)
20
-
21
- FileUtils.rm_rf payload_path
22
- message "IPA Built"
23
-
24
- #return ipa_path
25
- end
26
-
27
- def codesign
28
- # export CODESIGN_ALLOCATE=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/codesign_allocate
29
- # codesign –f –s “iPhone Developer: <insert your identity>” <appname>.app/<appname>
30
- #
31
- # You can check the Authorities by which an app is currently signed with the following command:
32
- #
33
- # codesign –d –vv <appname>.app/<appname>
34
- end
35
- end
36
- end
@@ -1,56 +0,0 @@
1
- require 'optparse'
2
-
3
- module AppSendr::Command
4
- class Deploy < Build ## Inherhits from Deploy
5
-
6
- def index
7
- #
8
- # opts = OptionParser.new
9
- # opts.on('-m', '--message') { output_version ; exit 0 }
10
- #
11
- # opts.parse!(args) rescue return false
12
- #
13
- #
14
- notify = option_exists?('--notify', false)
15
- is_public = option_exists?('--public', false)
16
-
17
- @configuration = load_configuration
18
- if require_project(1,"deploy","your active configuration name",true)
19
-
20
- @app_dir = path_to_app_dir
21
- @app_path = @app_dir+"/#{app_name}"
22
- if @app_name
23
-
24
- @notes = ask_for_notes
25
- message "Deploying #{@app_name}"
26
-
27
- message "Building IPA File"
28
-
29
- @ipa_path = build_ipa_with_app_at_path
30
- info = app_info
31
- bundle_identifier ||= info['CFBundleIdentifier']
32
- icon_path = icon_path(info)
33
- if is_file_to_large?(@ipa_path)
34
- upload_large_ipa(read_app_id,@ipa_path, profile_path,is_public,notify,@notes,bundle_identifier,icon_path)
35
- else
36
- message "Uploading IPA"
37
- appsendr.upload(read_app_id,@ipa_path, profile_path,is_public,notify,@notes,bundle_identifier,icon_path)
38
- message "App deployed"
39
-
40
- end
41
- FileUtils.rm_rf @ipa_path
42
- else
43
- error "No .app file was built for the configuration \"#{@configuration}\". Compile your app or use the \"build\" command."
44
- end
45
- end
46
- end
47
-
48
- protected
49
- def ask_for_notes
50
- print "Release Notes: "
51
- notes = ask
52
- return notes.strip
53
- end
54
-
55
- end
56
- end
@@ -1,63 +0,0 @@
1
- module AppSendr::Command
2
- class Groups < Base
3
- def index #output testers
4
- if in_project_dir?
5
-
6
- groups = appsendr.groups(read_app_id)
7
- if groups["message"].size > 0
8
- message "Your groups"
9
- i = 0
10
- display groups["message"].map {|app, id|
11
- "#{i+=1}. #{app['name'].ljust(35)}"
12
- }.join("\n")
13
- else
14
- display "You have no groups."
15
- end
16
- else
17
- error("You are not in a project directory")
18
- end
19
- end
20
-
21
- def add
22
- if in_project_dir?
23
- name = args.join(" ").strip
24
- appsendr.add_group(read_app_id,name)
25
- message "Group added"
26
- else
27
- error("You are not in a project directory")
28
- end
29
- end
30
-
31
- def remove
32
- if in_project_dir?
33
- name = args.join(" ").strip
34
- appsendr.remove_group(read_app_id,name)
35
- message "Removed group"
36
- else
37
- error("You are not in a project directory")
38
- end
39
- end
40
-
41
- def add_tester #<group> #<email>
42
-
43
- if in_project_dir?
44
- email = args.shift.strip
45
- name = args.join(" ").strip
46
- appsendr.add_group_tester(read_app_id,name, email)
47
- message "Added tester to group"
48
- else
49
- error("You are not in a project directory")
50
- end
51
- end
52
- def remove_tester #<group> #<email>
53
- if in_project_dir?
54
- email = args.shift.strip
55
- name = args.join(" ").strip
56
- appsendr.remove_group_tester(read_app_id,name, email)
57
- message "Added tester to group"
58
- else
59
- error("You are not in a project directory")
60
- end
61
- end
62
- end
63
- end
@@ -1,103 +0,0 @@
1
- module AppSendr::Command
2
- class Help < Base
3
- class HelpGroup < Array
4
- attr_reader :title
5
-
6
- def initialize(title)
7
- @title = title
8
- end
9
-
10
- def command(name, description)
11
- self << [name, description]
12
- end
13
-
14
- def space
15
- self << ['', '']
16
- end
17
- end
18
-
19
- def self.groups
20
- @groups ||= []
21
- end
22
-
23
- def self.group(title, &block)
24
- groups << begin
25
- group = HelpGroup.new(title)
26
- yield group
27
- group
28
- end
29
- end
30
-
31
- def self.create_default_groups!
32
- group 'Commands' do |group|
33
- group.command 'help', 'show this usage'
34
- group.command 'version', 'show the gem version'
35
- group.space
36
- group.command 'list', 'list your apps'
37
- group.command 'link', 'link your app with an exsiting one in appsendr'
38
- group.command 'create <name>', 'create a new app'
39
- group.space
40
- group.command 'url', 'get the latest version install url. pass --copy to copy to the clipboard'
41
- group.space
42
- group.command 'build <active configuration>', 'build your xcode project and deploy'
43
- group.command 'build:clean', 'clean your xcode project'
44
- group.space
45
- group.command 'deploy <active configuration>', 'deploy the current build. pass --notify to send notification to all testers'
46
- group.space
47
- group.command 'testers', 'list testers'
48
- group.command 'testers:add <email> <name>', 'add a tester'
49
- group.command 'testers:remove <email> ', 'remove a tester'
50
- group.command 'testers:clear', 'remove all testers'
51
- group.command 'testers:notify <group>', 'notify testers about the latest build. group is optional.'
52
- group.command 'testers:devices', 'devices of your testers'
53
-
54
- # group.space
55
- # group.command 'groups', 'list groups'
56
- # group.command 'groups:add <name>', 'add group'
57
- # group.command 'groups:remove <name>', 'remove group'
58
- # group.command 'groups:add_tester <email> <group name>', 'add tester to group'
59
- # group.command 'groups:remove_tester <email> <group name>', 'remove tester from group'
60
-
61
- group.space
62
- group.command 'collaborators', 'list collaborators'
63
- group.command 'collaborators:add <email>', 'add collaborators'
64
- group.command 'collaborators:remove <email>','remove collaborators'
65
- group.space
66
- group.command 'auth:login', 'reauthorize account'
67
- end
68
-
69
- end
70
-
71
- def index
72
- display usage
73
- end
74
-
75
- def version
76
- display AppSendr::Client.version
77
- end
78
-
79
- def usage
80
- longest_command_length = self.class.groups.map do |group|
81
- group.map { |g| g.first.length }
82
- end.flatten.max
83
-
84
- self.class.groups.inject(StringIO.new) do |output, group|
85
- output.puts "=== %s" % group.title
86
- output.puts
87
-
88
- group.each do |command, description|
89
- if command.empty?
90
- output.puts
91
- else
92
- output.puts "%-*s # %s" % [longest_command_length, command, description]
93
- end
94
- end
95
-
96
- output.puts
97
- output
98
- end.string
99
- end
100
- end
101
- end
102
-
103
- AppSendr::Command::Help.create_default_groups!
@@ -1,375 +0,0 @@
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