appsendr 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,115 @@
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 Heroku REST API. You might use this if you want to
9
+ # manage your Heroku apps from within a Ruby program, such as Capistrano.
10
+ #
11
+ # Example:
12
+ #
13
+ # require 'appsendr'
14
+ # droppr = AppSendr::Client.new('me@example.com', 'mypass')
15
+ # droppr.create('myapp')
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');
46
+ end
47
+
48
+ def list
49
+ JSON.parse(get('/api/app/list'))
50
+ end
51
+
52
+ def link(id)
53
+ post('/api/app/link',{:id=>id});
54
+ end
55
+
56
+ def create(name)
57
+ JSON.parse(post('/api/app/create', {:name=>name}))
58
+ end
59
+
60
+ def add_tester(app_id,email,name,udid=nil)
61
+ post('/api/app/add_tester/'+app_id.to_s,{:email=>email, :name=>name});
62
+ end
63
+
64
+ def remove_tester(app_id,email)
65
+ post('/api/app/remove_tester/'+app_id.to_s,{:email=>email});
66
+ end
67
+
68
+ def clear_testers(app_id)
69
+ delete('/api/app/clear_testers/'+app_id.to_s,{});
70
+ end
71
+
72
+ def testers(app_id)
73
+ JSON.parse(get('/api/app/testers/'+app_id.to_s))
74
+ end
75
+ def notify(app_id)
76
+ JSON.parse(get('/api/app/notify/'+app_id.to_s))
77
+ end
78
+
79
+ def upload(app_id, ipa,provisioning, notes, bundle_id, icon)
80
+ params = {
81
+ :ipa => File.new(ipa, 'rb'),
82
+ :profile => File.new(provisioning, 'rb'),
83
+ :notes=>notes,
84
+ :bundle_identifier => bundle_id,
85
+ :built_at=>Time.now
86
+ }
87
+ if icon
88
+ params[:icon] = File.new(icon,'rb')
89
+ end
90
+ post('/api/app/upload/'+app_id.to_s, params)
91
+ end
92
+
93
+ protected
94
+
95
+ def api_params(params={})
96
+ params[:api_key] ||= @api_key
97
+
98
+ end
99
+
100
+ def get(uri,params={})
101
+ params[:api_key] ||= @api_key
102
+ RestClient.get @resource.url+uri, {:params=>params}
103
+ #@resource[uri].get(api_params(params))#, :content_type => 'application/json')
104
+ end
105
+ def post(uri,params={})
106
+ params[:api_key] ||= @api_key
107
+ @resource[uri].post(params)#, :content_type => 'application/json')
108
+ end
109
+
110
+ def delete(uri,params={})
111
+ params[:api_key] ||= @api_key
112
+ print params
113
+ @resource[uri].delete(params)#, :content_type => 'application/json')
114
+ end
115
+ end
@@ -0,0 +1,68 @@
1
+ require 'appsendr/helpers'
2
+ require 'appsendr/binary_plist'
3
+ require 'appsendr/commands/base'
4
+
5
+ Dir["#{File.dirname(__FILE__)}/commands/*.rb"].each { |c| require c }
6
+
7
+ module AppSendr
8
+ module Command
9
+ class InvalidCommand < RuntimeError; end
10
+ class CommandFailed < RuntimeError; end
11
+
12
+ extend AppSendr::Helpers
13
+
14
+ class << self
15
+
16
+ def run(command, args, retries=0)
17
+ begin
18
+ run_internal 'auth:reauthorize', args.dup if (retries > 0 or !credentials_setup?)
19
+ run_internal(command, args.dup)
20
+ rescue InvalidCommand
21
+ error "Unknown command. Run 'appsendr help' for usage information."
22
+ rescue RestClient::Unauthorized
23
+ if retries < 3
24
+ STDERR.puts "Authentication failure"
25
+ run(command, args, retries+1)
26
+ else
27
+ error "Authentication failure"
28
+ end
29
+ rescue RestClient::RequestFailed
30
+ error "Something went wrong with the server."
31
+ rescue CommandFailed => e
32
+ error e.message
33
+ rescue Interrupt => e
34
+ error "\n[canceled]"
35
+ end
36
+ end
37
+
38
+ def run_internal(command, args, appsendr=nil)
39
+ klass, method = parse(command)
40
+ runner = klass.new(args, appsendr)
41
+ raise InvalidCommand unless runner.respond_to?(method)
42
+ runner.send(method)
43
+ end
44
+
45
+ def parse(command)
46
+ parts = command.split(':')
47
+ case parts.size
48
+ when 1
49
+ begin
50
+ return eval("AppSendr::Command::#{command.capitalize}"), :index
51
+ rescue NameError, NoMethodError
52
+ return AppSendr::Command::App, command.to_sym
53
+ end
54
+ else
55
+ begin
56
+ const = AppSendr::Command
57
+ command = parts.pop
58
+ parts.each { |part| const = const.const_get(part.capitalize) }
59
+ return const, command.to_sym
60
+ rescue NameError
61
+ raise InvalidCommand
62
+ end
63
+ end
64
+ end
65
+
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,167 @@
1
+ require 'readline'
2
+ #require 'launchy'
3
+
4
+ module AppSendr::Command
5
+ class App < Base
6
+ attr_accessor :app_id, :app_name, :app
7
+
8
+ def list
9
+ list = appsendr.list
10
+ if list["message"].size > 0
11
+ display "=== Your apps"
12
+ i = 0
13
+ display list["message"].map {|app, id|
14
+ "#{i+=1}. #{app['app']['name'].ljust(35)}"
15
+ }.join("\n")
16
+ else
17
+ display "You have no apps."
18
+ end
19
+ end
20
+
21
+ def create
22
+ # username = extract_option('--username')
23
+ # display username
24
+ # password = extract_option('--password')
25
+ # display password
26
+ #
27
+
28
+ if require_in_project_and_no_droppr(1,"an app name","a name to create an app on appsendr",true)
29
+ name = args.join(" ").strip
30
+ resp = appsendr.create(name)
31
+ make_appsendr_dir if resp['message']
32
+ @app = [resp['message']['app']['id'],resp['message']['app']['name']]
33
+ write_app
34
+ display "Your app has been created."
35
+
36
+ end
37
+ end
38
+
39
+ def link
40
+ if in_project_dir?
41
+ error "This app is already linked to an appsendr." unless !has_project_droppr?
42
+
43
+ app = list_apps_and_ask
44
+ return unless app
45
+
46
+ link = appsendr.link(app['id'])
47
+ make_appsendr_dir if link
48
+ @app = [app['id'],app['name']]
49
+ write_app
50
+ display "Your app has been linked."
51
+
52
+ else
53
+ error("You are not in a project")
54
+ end
55
+ end
56
+
57
+ def list_apps_and_ask
58
+ apps = []
59
+ lines = []
60
+ list = appsendr.list
61
+ if list["message"].size > 0
62
+ display "=== Your apps"
63
+ i = 0
64
+ list["message"].each{|app|
65
+ apps.push({"name"=>app['app']['name'], "id"=>app['app']['id']})
66
+ lines.push("#{i+=1}. #{app['app']['name'].ljust(35)}")
67
+ }
68
+ display lines.join("\n")
69
+ print "\n"
70
+ print "Enter the # for the app you wish to link to: "
71
+ app_number = ask
72
+
73
+ return apps[app_number.to_i-1]
74
+
75
+ else
76
+ display "You have no apps."
77
+ end
78
+ end
79
+
80
+ # def info
81
+ # name = (args.first && !args.first =~ /^\-\-/) ? args.first : extract_app
82
+ # attrs = appsendr.info(name)
83
+ #
84
+ # attrs[:web_url] ||= "http://#{attrs[:name]}.#{heroku.host}/"
85
+ # attrs[:git_url] ||= "git@#{heroku.host}:#{attrs[:name]}.git"
86
+ #
87
+ # display "=== #{attrs[:name]}"
88
+ # display "Web URL: #{attrs[:web_url]}"
89
+ # display "Domain name: http://#{attrs[:domain_name]}/" if attrs[:domain_name]
90
+ # display "Git Repo: #{attrs[:git_url]}"
91
+ # display "Dynos: #{attrs[:dynos]}"
92
+ # display "Workers: #{attrs[:workers]}"
93
+ # display "Repo size: #{format_bytes(attrs[:repo_size])}" if attrs[:repo_size]
94
+ # display "Slug size: #{format_bytes(attrs[:slug_size])}" if attrs[:slug_size]
95
+ # display "Stack: #{attrs[:stack]}" if attrs[:stack]
96
+ # if attrs[:database_size]
97
+ # data = format_bytes(attrs[:database_size])
98
+ # if tables = attrs[:database_tables]
99
+ # data = data.gsub('(empty)', '0K') + " in #{quantify("table", tables)}"
100
+ # end
101
+ # display "Data size: #{data}"
102
+ # end
103
+ #
104
+ # if attrs[:cron_next_run]
105
+ # display "Next cron: #{format_date(attrs[:cron_next_run])} (scheduled)"
106
+ # end
107
+ # if attrs[:cron_finished_at]
108
+ # display "Last cron: #{format_date(attrs[:cron_finished_at])} (finished)"
109
+ # end
110
+ #
111
+ # unless attrs[:addons].empty?
112
+ # display "Addons: " + attrs[:addons].map { |a| a['description'] }.join(', ')
113
+ # end
114
+ #
115
+ # display "Owner: #{attrs[:owner]}"
116
+ # collaborators = attrs[:collaborators].delete_if { |c| c[:email] == attrs[:owner] }
117
+ # unless collaborators.empty?
118
+ # first = true
119
+ # lead = "Collaborators:"
120
+ # attrs[:collaborators].each do |collaborator|
121
+ # display "#{first ? lead : ' ' * lead.length} #{collaborator[:email]}"
122
+ # first = false
123
+ # end
124
+ # end
125
+ #
126
+ # if attrs[:create_status] != "complete"
127
+ # display "Create Status: #{attrs[:create_status]}"
128
+ # end
129
+ # end
130
+
131
+ protected
132
+
133
+ def make_appsendr_dir
134
+ if in_project_dir?
135
+ FileUtils.mkdir_p(Dir.pwd+"/"+AppSendr::PROJECT_DIR)
136
+ end
137
+ end
138
+
139
+ def get_app
140
+ return if @app
141
+ unless @app = read_app
142
+ end
143
+ @app
144
+ end
145
+
146
+ def get_app_id
147
+ return if @app_id
148
+ unless @app_id = read_app_id
149
+ end
150
+ @app_id
151
+ end
152
+
153
+
154
+ def write_app
155
+ FileUtils.mkdir_p(File.dirname(project_appsendr_app))
156
+ File.open(project_appsendr_app, 'w') do |f|
157
+ f.puts self.app.join("\n")
158
+ end
159
+ set_app_permissions
160
+ end
161
+
162
+ def set_app_permissions
163
+ FileUtils.chmod 0700, File.dirname(project_appsendr_app)
164
+ FileUtils.chmod 0600, project_appsendr_app
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,155 @@
1
+ module AppSendr::Command
2
+ class Auth < Base
3
+ attr_accessor :credentials
4
+
5
+ def client
6
+ @client ||= init_for_credentials
7
+ end
8
+
9
+ def init_for_credentials
10
+ client = AppSendr::Client.new(api_key, host)
11
+ #client.on_warning { |msg| self.display("\n#{msg}\n\n") }
12
+ client
13
+ end
14
+
15
+ def auth_credentials(user,pass)
16
+ AppSendr::Client.auth(user,pass,host)
17
+ end
18
+
19
+ # just a stub; will raise if not authenticated
20
+ def check
21
+ client.list
22
+ end
23
+
24
+ def host
25
+ ENV['APPDROPPR_HOST'] || 'appsendr.com' ##'appsendr.heroku.com' #
26
+ end
27
+
28
+ def reauthorize
29
+ user_pass = ask_for_credentials
30
+ @credentials = auth_credentials(user_pass[0],user_pass[1])
31
+ write_credentials
32
+ end
33
+
34
+ def api_key
35
+ get_credentials
36
+ @credentials[0]
37
+ end
38
+
39
+ # def user # :nodoc:
40
+ # get_credentials
41
+ # @credentials[0]
42
+ # end
43
+ #
44
+ # def password # :nodoc:
45
+ # get_credentials
46
+ # @credentials[1]
47
+ # end
48
+
49
+ def get_credentials # :nodoc:
50
+ return if @credentials
51
+ unless @credentials = read_credentials
52
+ user_pass = ask_for_credentials
53
+ @credentials = auth_credentials(user_pass[0],user_pass[1])
54
+
55
+ #@credentials = ask_for_credentials
56
+ save_credentials
57
+ end
58
+ @credentials
59
+ end
60
+
61
+ def read_credentials
62
+ File.exists?(credentials_file) and File.read(credentials_file).split("\n")
63
+ end
64
+
65
+ def echo_off
66
+ system "stty -echo"
67
+ end
68
+
69
+ def echo_on
70
+ system "stty echo"
71
+ end
72
+
73
+ def ask_for_credentials
74
+ puts "Enter your AppSendr credentials."
75
+
76
+ print "Username: "
77
+ user = ask
78
+
79
+ print "Password: "
80
+ password = running_on_windows? ? ask_for_password_on_windows : ask_for_password
81
+
82
+ [ user, password ]
83
+ end
84
+
85
+ def ask_for_password_on_windows
86
+ require "Win32API"
87
+ char = nil
88
+ password = ''
89
+
90
+ while char = Win32API.new("crtdll", "_getch", [ ], "L").Call do
91
+ break if char == 10 || char == 13 # received carriage return or newline
92
+ if char == 127 || char == 8 # backspace and delete
93
+ password.slice!(-1, 1)
94
+ else
95
+ # windows might throw a -1 at us so make sure to handle RangeError
96
+ (password << char.chr) rescue RangeError
97
+ end
98
+ end
99
+ puts
100
+ return password
101
+ end
102
+
103
+ def ask_for_password
104
+ echo_off
105
+ password = ask
106
+ puts
107
+ echo_on
108
+ return password
109
+ end
110
+
111
+ def save_credentials
112
+ begin
113
+ write_credentials
114
+ #command = args.any? { |a| a == '--ignore-keys' } ? 'auth:check' : 'keys:add'
115
+ command = 'auth:check'
116
+ AppSendr::Command.run_internal(command, args)
117
+ rescue RestClient::Unauthorized => e
118
+ delete_credentials
119
+ raise e unless retry_login?
120
+
121
+ display "\nAuthentication failed"
122
+ user_pass = ask_for_credentials
123
+ @credentials = auth_credentials(user_pass[0],user_pass[1])
124
+ @client = init_for_credentials
125
+ retry
126
+ rescue Exception => e
127
+ delete_credentials
128
+ raise e
129
+ end
130
+ end
131
+
132
+ def retry_login?
133
+ @login_attempts ||= 0
134
+ @login_attempts += 1
135
+ @login_attempts < 3
136
+ end
137
+
138
+ def write_credentials
139
+ FileUtils.mkdir_p(File.dirname(credentials_file))
140
+ File.open(credentials_file, 'w') do |f|
141
+ f.puts self.credentials
142
+ end
143
+ set_credentials_permissions
144
+ end
145
+
146
+ def set_credentials_permissions
147
+ FileUtils.chmod 0700, File.dirname(credentials_file)
148
+ FileUtils.chmod 0600, credentials_file
149
+ end
150
+
151
+ def delete_credentials
152
+ FileUtils.rm_f(credentials_file)
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,63 @@
1
+ require 'fileutils'
2
+
3
+ module AppSendr::Command
4
+ class Base
5
+ include AppSendr::Helpers
6
+
7
+ attr_accessor :args
8
+ attr_reader :autodetected_app
9
+ def initialize(args, appsendr=nil)
10
+ @args = args
11
+ @appsendr = appsendr
12
+ @autodetected_app = false
13
+ end
14
+
15
+ def confirm(message="Are you sure you wish to continue? (y/n)?")
16
+ display("#{message} ", false)
17
+ ask.downcase == 'y'
18
+ end
19
+
20
+ def format_date(date)
21
+ date = Time.parse(date) if date.is_a?(String)
22
+ date.strftime("%Y-%m-%d %H:%M %Z")
23
+ end
24
+
25
+ def ask
26
+ gets.strip
27
+ end
28
+
29
+ def appsendr
30
+ @appsendr ||= AppSendr::Command.run_internal('auth:client', args)
31
+ end
32
+
33
+ def extract_app(force=true)
34
+ app = extract_option('--app', false)
35
+ raise(CommandFailed, "You must specify an app name after --app") if app == false
36
+ unless app
37
+ app = extract_app_in_dir(Dir.pwd) ||
38
+ raise(CommandFailed, "No app specified.\nRun this command from app folder or set it adding --app <app name>") if force
39
+ @autodetected_app = true
40
+ end
41
+ app
42
+ end
43
+
44
+ def extract_option(options, default=true)
45
+ values = options.is_a?(Array) ? options : [options]
46
+ return unless opt_index = args.select { |a| values.include? a }.first
47
+ opt_position = args.index(opt_index) + 1
48
+ if args.size > opt_position && opt_value = args[opt_position]
49
+ if opt_value.include?('--')
50
+ opt_value = nil
51
+ else
52
+ args.delete_at(opt_position)
53
+ end
54
+ end
55
+ opt_value ||= default
56
+ args.delete(opt_index)
57
+ block_given? ? yield(opt_value) : opt_value
58
+ end
59
+
60
+ end
61
+
62
+
63
+ end