appsendr 0.0.1

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,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