appsendr 0.0.6 → 1.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.
@@ -1,258 +0,0 @@
1
- require 'rubygems'
2
- require 'rest_client'
3
- require 'uri'
4
- require 'time'
5
- require 'appsendr/constants'
6
- require 'json/pure' unless {}.respond_to?(:to_json)
7
-
8
- # A Ruby class to call the AppSendr REST API.
9
- #
10
- # Example:
11
- #
12
- # require 'appsendr'
13
- # droppr = AppSendr::Client.new('me@example.com', 'mypass')
14
- # droppr.create('myapp')
15
- #
16
-
17
- class AppSendr::Client
18
- def self.version
19
- AppSendr::VERSION
20
- end
21
-
22
- def self.gem_version_string
23
- "appsendr-gem/#{version}"
24
- end
25
-
26
- def self.auth(user,pass,host)
27
- @resource = RestClient::Resource.new "http://#{host}"
28
- resp = @resource['/api/user/authenticate'].post({:username=>user, :password=>pass})
29
- json_resp = JSON.parse(resp)
30
- return nil unless json_resp
31
- return json_resp["message"] if json_resp["status"].to_i == 200
32
-
33
- end
34
-
35
- attr_reader :host, :user, :password, :api_key
36
-
37
- def initialize(api_key,host='appsendr.com')
38
- @api_key = api_key
39
- @host = host
40
- @resource = RestClient::Resource.new "http://#{@host}"
41
-
42
- end
43
-
44
- def authenticate
45
- post('/api/user/authenticate.json');
46
- end
47
-
48
- def list
49
- get('/api/user/apps.json')
50
- end
51
-
52
- def link(id)
53
- get('/api/app/show/'+id.to_s+'.json');
54
- end
55
- def current_version(app_id)
56
- get('/api/app/versions/'+app_id.to_s+'.json',{:limit=>1})
57
- end
58
-
59
- def versions(app_id)
60
- get('/api/app/versions/'+app_id.to_s+'.json')
61
- end
62
-
63
- def create(name)
64
- post('/api/app/create.json', {:name=>name})
65
- end
66
-
67
- def devices(app_id,plist=true)
68
- format = "json"
69
- format = "plist" if plist
70
-
71
- if app_id
72
- get("/api/tester/devices.#{format}", {:app_id=>app_id})
73
- else
74
- get("/api/tester/devices.#{format}")
75
- end
76
- end
77
-
78
- def add_tester(app_id,email,name,udid=nil)
79
- post('/api/tester/create.json',{:email=>email, :name=>name, :app_id=>app_id.to_s});
80
- end
81
-
82
- def remove_tester(app_id,email)
83
- post('/api/tester/destroy.json',{:email=>email, :app_id=>app_id.to_s});
84
- end
85
-
86
- def clear_testers(app_id)
87
- delete('/api/tester/clear.json',{:app_id=>app_id.to_s});
88
- end
89
-
90
- def add_group(app_id,name)
91
- post('/api/group/create.json',{:name=>name,:app_id=>app_id.to_s});
92
- end
93
-
94
- def remove_group(app_id, name)
95
- post('/api/group/destroy.json',{:name=>name,:app_id=>app_id.to_s});
96
- end
97
-
98
- def groups(app_id)
99
- get('/api/app/groups/'+app_id.to_s+'.json')
100
- end
101
-
102
-
103
- def add_group_tester(app_id,name, email)
104
- post('/api/group/add_tester.json',{:name=>name,:app_id=>app_id.to_s, :email=>email});
105
- end
106
-
107
- def remove_group_tester(app_id, name, email)
108
- post('/api/group/remove_tester.json',{:name=>name,:app_id=>app_id.to_s, :email=>email});
109
- end
110
-
111
-
112
- ### add_collaborator
113
-
114
- def add_collaborator(app_id,emaill)
115
- post('/api/collaborator/create.json',{:email=>email,:app_id=>app_id.to_s});
116
- end
117
-
118
- def remove_collaborator(app_id,email)
119
- post('/api/collaborator/destroy.json',{:email=>email,:app_id=>app_id.to_s});
120
- end
121
-
122
- def collaborators(app_id)
123
- get('/api/app/collaborators/'+app_id.to_s+'.json')
124
- end
125
-
126
- def testers(app_id)
127
- get('/api/app/testers/'+app_id.to_s+'.json')
128
- end
129
- def notify(app_id,group=nil)
130
- post('/api/app/notify/'+app_id.to_s+'.json',{:group=>group})
131
- end
132
-
133
- def upload(app_id, ipa,provisioning, is_public, notify, notes, bundle_id, icon, later=false)
134
- params = {
135
- :provisioning_profile => File.new(provisioning, 'rb'),
136
- :notes=>notes,
137
- :bundle_identifier => bundle_id,
138
- :built_at=>Time.now,
139
- :notify=>notify,
140
- :public=>is_public,
141
- :app_id => app_id.to_s
142
- # :ipa=>
143
- }
144
- if icon
145
- params[:icon] = File.new(icon,'rb')
146
- end
147
-
148
- if later
149
- params[:upload_later] = File.basename(ipa)
150
-
151
- else
152
- params[:ipa] = File.new(ipa, 'rb')
153
-
154
- end
155
-
156
- post('/api/version/create.json', {:version=>params})
157
- end
158
-
159
- def initialize_multipart(app_id, version_id, ipa)
160
- ipa_name = File.basename(ipa)
161
-
162
- params = {}
163
- params[:ipa_path] = upload_path(app_id,version_id,ipa_name)
164
- params[:ipa_name] = ipa_name
165
- params[:id] = version_id
166
-
167
- post('/api/version/multipart_init.json', {:version=>params})
168
- end
169
-
170
- def upload_part(app_id, version_id, ipa, upload_id, part_number, chunk)
171
- ipa_name = File.basename(ipa)
172
- ipa_size = File.size(ipa)
173
-
174
- params = {}
175
- params[:ipa_chunk] = File.new(chunk,'rb')
176
- params[:ipa_size] = ipa_size
177
- params[:ipa_path] = upload_path(app_id,version_id,ipa_name)
178
- params[:upload_id] = upload_id
179
- params[:part_number] = part_number
180
- params[:id] = version_id
181
-
182
- retryable( :tries => 5 ) do
183
- resp = post('/api/version/multipart_upload_part.json', {:version=>params})
184
- return resp
185
- end
186
- end
187
-
188
- def finish_multipart(app_id, version_id, ipa, upload_id, parts, notify)
189
- ipa_name = File.basename(ipa)
190
- ipa_size = File.size(ipa)
191
-
192
- params = {}
193
- params[:ipa_size] = ipa_size
194
- params[:ipa_path] = upload_path(app_id,version_id,ipa_name)
195
- params[:upload_id] = upload_id
196
- params[:parts] = parts
197
- params[:id] = version_id
198
- params[:notify] = notify
199
- post('/api/version/multipart_finished.json', {:version=>params})
200
-
201
-
202
- end
203
-
204
- def abort_multipart(app_id, version_id, ipa, upload_id)
205
- params = { :version=> {
206
- :ipa_path=>upload_path(app_id,version_id,File.basename(ipa)),
207
- :upload_id=>upload_id,
208
- :id=>version_id
209
- }
210
- }
211
- post('/api/version/abort_multipart.json',params)
212
-
213
- end
214
-
215
- protected
216
-
217
-
218
- def upload_path(app_id,version_id,ipa_name)
219
- "app/#{app_id}/#{version_id}/ipa/#{ipa_name}"
220
- end
221
-
222
- def api_params(params={})
223
- params[:api_key] ||= @api_key
224
-
225
- end
226
-
227
- def retryable(options = {}, &block)
228
- opts = { :tries => 1, :on => Exception }.merge(options)
229
-
230
- retry_exception, retries = opts[:on], opts[:tries]
231
-
232
- begin
233
- return yield
234
- rescue retry_exception
235
- retry if (retries -= 1) > 0
236
- end
237
-
238
- yield
239
- end
240
-
241
- def get(uri,params={})
242
- params[:api_key] ||= @api_key
243
- resp = RestClient.get @resource.url+uri, {:params=>params}
244
- JSON.parse(resp)
245
- #@resource[uri].get(api_params(params))#, :content_type => 'application/json')
246
- end
247
- def post(uri,params={})
248
- params[:api_key] ||= @api_key
249
- resp = @resource[uri].post(params)#, :content_type => 'application/json')
250
- JSON.parse(resp)
251
- end
252
-
253
- def delete(uri,params={})
254
- params[:api_key] ||= @api_key
255
- print params
256
- @resource[uri].delete(params)#, :content_type => 'application/json')
257
- end
258
- end
@@ -1,81 +0,0 @@
1
- require 'appsendr/helpers'
2
- require 'appsendr/binary_plist'
3
- require 'appsendr/commands/base'
4
- require 'appsendr/progressbar'
5
-
6
- Dir["#{File.dirname(__FILE__)}/commands/*.rb"].each { |c| require c }
7
-
8
- module AppSendr
9
- module Command
10
- class InvalidCommand < RuntimeError; end
11
- class CommandFailed < RuntimeError; end
12
-
13
- extend AppSendr::Helpers
14
-
15
- class << self
16
-
17
- def run(command, args, retries=0)
18
- begin
19
- run_internal 'auth:login', args.dup if (retries > 0 or !credentials_setup?)
20
- run_internal(command, args.dup)
21
- rescue InvalidCommand
22
- error "Unknown command. Run 'appsendr help' for usage information."
23
- rescue RestClient::Unauthorized
24
- if retries < 3
25
- STDERR.puts "Authentication failure"
26
- run(command, args, retries+1)
27
- else
28
- error "Authentication failure"
29
- end
30
- rescue RestClient::ResourceNotFound
31
- error "Record not found. Are you allowed to access this record?"
32
- rescue RestClient::RequestFailed => e
33
- begin
34
- response = JSON.parse(e.http_body)
35
- display "=== Errors"
36
- response['message'].each{|err|
37
- display err.join(" ")
38
- }
39
- rescue
40
- error "Something went wrong with the server."
41
- end
42
- rescue CommandFailed => e
43
- error e.message
44
- rescue Interrupt => e
45
- error "\n[canceled]"
46
- rescue Exception => e
47
- error e
48
- end
49
- end
50
-
51
- def run_internal(command, args, appsendr=nil)
52
- klass, method = parse(command)
53
- runner = klass.new(args, appsendr)
54
- raise InvalidCommand unless runner.respond_to?(method)
55
- runner.send(method)
56
- end
57
-
58
- def parse(command)
59
- parts = command.split(':')
60
- case parts.size
61
- when 1
62
- begin
63
- return eval("AppSendr::Command::#{command.capitalize}"), :index
64
- rescue NameError, NoMethodError
65
- return AppSendr::Command::App, command.to_sym
66
- end
67
- else
68
- begin
69
- const = AppSendr::Command
70
- command = parts.pop
71
- parts.each { |part| const = const.const_get(part.capitalize) }
72
- return const, command.to_sym
73
- rescue NameError
74
- raise InvalidCommand
75
- end
76
- end
77
- end
78
-
79
- end
80
- end
81
- end
@@ -1,241 +0,0 @@
1
- require 'readline'
2
-
3
- module AppSendr::Command
4
- class App < Base
5
- attr_accessor :app_id, :app_name, :app
6
-
7
- def list
8
- list = appsendr.list
9
- if list["message"].size > 0
10
- message "Your apps"
11
- i = 0
12
- display list["message"].map {|app, id|
13
- "#{i+=1}. #{app['name'].ljust(35)}"
14
- }.join("\n")
15
- else
16
- display "You have no apps."
17
- end
18
- end
19
-
20
- def create
21
- # username = extract_option('--username')
22
- # display username
23
- # password = extract_option('--password')
24
- # display password
25
- #
26
- name = args.join(" ").strip
27
-
28
- error "You must include an app name" if name.empty?
29
- require_project_dir("add an app")
30
-
31
- if has_project_droppr?
32
- list = appsendr.list
33
- if list["message"].size > 0
34
- list["message"].each{|app|
35
- if app['name'] == name
36
- remove_appsendr_dir
37
- make_appsendr_dir
38
-
39
- @app = [ app['id'], app['name']]
40
- write_app
41
- display "Your app has been linked."
42
- return
43
- end
44
- }
45
-
46
- resp = appsendr.create(name)
47
- remove_appsendr_dir
48
- make_appsendr_dir if resp['message']
49
- @app = [resp['message']['id'],resp['message']['name']]
50
- write_app
51
- display "Your app has been created."
52
-
53
- else
54
- error "This app is linked to another account's app"
55
- end
56
- else
57
- resp = appsendr.create(name)
58
- make_appsendr_dir if resp['message']
59
- @app = [resp['message']['id'],resp['message']['name']]
60
- write_app
61
- display "Your app has been created."
62
- end
63
-
64
- end
65
-
66
- def url
67
- if in_project_dir?
68
-
69
- resp = appsendr.current_version(get_app_id)
70
- if resp["message"].size > 0
71
- message "Your last install url is:"
72
- url = resp["message"][0]['install_url']
73
- display url
74
- do_copy = option_exists?('--copy', false)
75
- if do_copy
76
- display "Copied URL"
77
- IO.popen('pbcopy', 'w') { |clipboard| clipboard.write url }
78
- end
79
- else
80
- display "No versions available"
81
- end
82
- else
83
- error("You are not in a project directory")
84
- end
85
-
86
- end
87
-
88
- def link
89
- if in_project_dir?
90
- error "This app is already linked to an appsendr." unless !has_project_droppr?
91
-
92
- app = list_apps_and_ask
93
- return unless app
94
-
95
- link = appsendr.link(app['id'])
96
- make_appsendr_dir if link
97
- name = link['message']['name'] || app['name']
98
- aid = link['message']['id'] || app['id']
99
-
100
- @app = [aid,name]
101
- write_app
102
- display "Your app has been linked."
103
-
104
- else
105
- error("You are not in a project directory")
106
- end
107
- end
108
-
109
- def list_apps_and_ask
110
- apps = []
111
- lines = []
112
- list = appsendr.list
113
- if list["message"].size > 0
114
- message "Your apps"
115
- i = 0
116
- list["message"].each{|app|
117
- apps.push({"name"=>app['name'], "id"=>app['id']})
118
- lines.push("#{i+=1}. #{app['name'].ljust(35)}")
119
- }
120
- display lines.join("\n")
121
- print "\n"
122
- print "Enter the # for the app you wish to link to: "
123
- app_number = ask
124
-
125
- return apps[app_number.to_i-1]
126
-
127
- else
128
- display "You have no apps."
129
- end
130
- end
131
-
132
- def versions
133
- if in_project_dir?
134
-
135
- groups = appsendr.versions(get_app_id)
136
- if groups["message"].size > 0
137
- message "Your versions"
138
- i = 0
139
- display groups["message"].map {|app, id|
140
- "#{i+=1}. #{app['notes']} #{app['install_url']}"
141
- }.join("\n")
142
- else
143
- display "You have no version."
144
- end
145
- else
146
- error("You are not in a project directory")
147
- end
148
- end
149
- # def info
150
- # name = (args.first && !args.first =~ /^\-\-/) ? args.first : extract_app
151
- # attrs = appsendr.info(name)
152
- #
153
- # attrs[:web_url] ||= "http://#{attrs[:name]}.#{heroku.host}/"
154
- # attrs[:git_url] ||= "git@#{heroku.host}:#{attrs[:name]}.git"
155
- #
156
- # display "=== #{attrs[:name]}"
157
- # display "Web URL: #{attrs[:web_url]}"
158
- # display "Domain name: http://#{attrs[:domain_name]}/" if attrs[:domain_name]
159
- # display "Git Repo: #{attrs[:git_url]}"
160
- # display "Dynos: #{attrs[:dynos]}"
161
- # display "Workers: #{attrs[:workers]}"
162
- # display "Repo size: #{format_bytes(attrs[:repo_size])}" if attrs[:repo_size]
163
- # display "Slug size: #{format_bytes(attrs[:slug_size])}" if attrs[:slug_size]
164
- # display "Stack: #{attrs[:stack]}" if attrs[:stack]
165
- # if attrs[:database_size]
166
- # data = format_bytes(attrs[:database_size])
167
- # if tables = attrs[:database_tables]
168
- # data = data.gsub('(empty)', '0K') + " in #{quantify("table", tables)}"
169
- # end
170
- # display "Data size: #{data}"
171
- # end
172
- #
173
- # if attrs[:cron_next_run]
174
- # display "Next cron: #{format_date(attrs[:cron_next_run])} (scheduled)"
175
- # end
176
- # if attrs[:cron_finished_at]
177
- # display "Last cron: #{format_date(attrs[:cron_finished_at])} (finished)"
178
- # end
179
- #
180
- # unless attrs[:addons].empty?
181
- # display "Addons: " + attrs[:addons].map { |a| a['description'] }.join(', ')
182
- # end
183
- #
184
- # display "Owner: #{attrs[:owner]}"
185
- # collaborators = attrs[:collaborators].delete_if { |c| c[:email] == attrs[:owner] }
186
- # unless collaborators.empty?
187
- # first = true
188
- # lead = "Collaborators:"
189
- # attrs[:collaborators].each do |collaborator|
190
- # display "#{first ? lead : ' ' * lead.length} #{collaborator[:email]}"
191
- # first = false
192
- # end
193
- # end
194
- #
195
- # if attrs[:create_status] != "complete"
196
- # display "Create Status: #{attrs[:create_status]}"
197
- # end
198
- # end
199
-
200
- protected
201
- def remove_appsendr_dir
202
- if in_project_dir?
203
- FileUtils.rm_r(Dir.pwd+"/"+AppSendr::PROJECT_DIR)
204
- end
205
- end
206
-
207
- def make_appsendr_dir
208
- if in_project_dir?
209
- FileUtils.mkdir_p(Dir.pwd+"/"+AppSendr::PROJECT_DIR)
210
- end
211
- end
212
-
213
- def get_app
214
- return if @app
215
- unless @app = read_app
216
- end
217
- @app
218
- end
219
-
220
- def get_app_id
221
- return if @app_id
222
- unless @app_id = read_app_id
223
- end
224
- @app_id
225
- end
226
-
227
-
228
- def write_app
229
- FileUtils.mkdir_p(File.dirname(project_appsendr_app))
230
- File.open(project_appsendr_app, 'w') do |f|
231
- f.puts self.app.join("\n")
232
- end
233
- set_app_permissions
234
- end
235
-
236
- def set_app_permissions
237
- FileUtils.chmod 0700, File.dirname(project_appsendr_app)
238
- FileUtils.chmod 0600, project_appsendr_app
239
- end
240
- end
241
- end