jiveapps 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,68 @@
1
+ require 'jiveapps/helpers'
2
+ require 'jiveapps/commands/base'
3
+
4
+ Dir["#{File.dirname(__FILE__)}/commands/*.rb"].each { |c| require c }
5
+
6
+ module Jiveapps
7
+ module Command
8
+ class InvalidCommand < RuntimeError; end
9
+ class CommandFailed < RuntimeError; end
10
+
11
+ extend Jiveapps::Helpers
12
+
13
+ class << self
14
+
15
+ def run(command, args, retries=0)
16
+ begin
17
+ run_internal 'auth:reauthorize', args.dup if retries > 0
18
+ run_internal(command, args.dup)
19
+ rescue InvalidCommand
20
+ error "Unknown command. Run 'jiveapps help' for usage information."
21
+ rescue RestClient::Unauthorized
22
+ if retries < 3
23
+ STDERR.puts "Authentication failure"
24
+ run(command, args, retries+1)
25
+ else
26
+ error "Authentication failure"
27
+ end
28
+ end
29
+ end
30
+
31
+ def run_internal(command, args)
32
+ klass, method = parse(command)
33
+ runner = klass.new(args)
34
+ raise InvalidCommand unless runner.respond_to?(method)
35
+ runner.send(method)
36
+ end
37
+
38
+ def parse(command)
39
+ parts = command.split(':')
40
+ case parts.size
41
+ when 1
42
+ begin
43
+ return eval("Jiveapps::Command::#{command.capitalize}"), :index
44
+ rescue NameError, NoMethodError
45
+ return Jiveapps::Command::App, command
46
+ end
47
+ when 2
48
+ begin
49
+ return Jiveapps::Command.const_get(parts[0].capitalize), parts[1]
50
+ rescue NameError
51
+ raise InvalidCommand
52
+ end
53
+ else
54
+ raise InvalidCommand
55
+ end
56
+ end
57
+
58
+
59
+ ### Helpers
60
+
61
+ def error(msg)
62
+ STDERR.puts(msg)
63
+ exit 1
64
+ end
65
+
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,160 @@
1
+ module Jiveapps::Command
2
+ class App < Base
3
+
4
+ attr_reader :current_app
5
+
6
+ def list
7
+ formatted_list = jiveapps.list.map do |app|
8
+ " - " + app['name']
9
+ end
10
+
11
+ if formatted_list.size > 0
12
+ display "Your apps:"
13
+ display formatted_list
14
+ else
15
+ display "You have no apps."
16
+ end
17
+ end
18
+
19
+ def info
20
+ if app_name == nil
21
+ display "No app specified."
22
+ display "Run this command from app folder or set it by running: jiveapps info <app name>"
23
+ else
24
+ app = jiveapps.info(app_name)
25
+ if app == nil
26
+ display "App not found."
27
+ else
28
+ display "=== #{app['name']}"
29
+ display "Git URL: #{app['git_url']}"
30
+ display "App URL: #{app['app_url']}"
31
+ display "Sandbox Canvas URL: #{app['sandbox_canvas_url']}"
32
+ display "Sandbox Dashboard URL: #{app['sandbox_dashboard_url']}"
33
+ display "OAuth Consumer Key: #{app['oauth_consumer_key']}"
34
+ display "OAuth Consumer Secret: #{app['oauth_consumer_secret']}"
35
+ end
36
+ end
37
+ end
38
+
39
+ def create
40
+ # check auth credentials and ssh key before generating app
41
+ Jiveapps::Command.run_internal('auth:check', [])
42
+ create_remote_app
43
+ generate_app
44
+ create_local_git_repo_and_push_to_remote
45
+ register_app
46
+ create_notify_user
47
+ end
48
+
49
+ def version
50
+ puts Jiveapps::Client.version
51
+ end
52
+
53
+ private
54
+
55
+ def debug_mode?
56
+ return @debug_mode if @debug_mode.nil? == false
57
+
58
+ if args.include?('--debug')
59
+ args.delete('--debug')
60
+ @debug_mode = true
61
+ else
62
+ @debug_mode = false
63
+ end
64
+ end
65
+
66
+ def create_remote_app
67
+ debug "Creating remote app."
68
+ @current_app = jiveapps.create(app_name)
69
+ if @current_app["errors"]
70
+ if @current_app["errors"]["name"]
71
+ display "Error: Name #{@current_app["errors"]["name"]}"
72
+ end
73
+ @current_app = nil
74
+ end
75
+ end
76
+
77
+ def generate_app
78
+ return unless current_app
79
+ debug "Generating local app."
80
+
81
+ require 'rubygems'
82
+ require 'rubigen'
83
+ require 'rubigen/scripts/generate'
84
+ RubiGen::Base.use_application_sources!
85
+ RubiGen::Scripts::Generate.new.run(@args, :generator => 'create')
86
+ end
87
+
88
+ def create_local_git_repo_and_push_to_remote
89
+ return unless current_app
90
+ debug "Creating local git repo and pushing to remote."
91
+
92
+ Dir.chdir(File.join(Dir.pwd, app_name)) do
93
+
94
+ run("git init")
95
+ run("git add .")
96
+ run('git commit -m "initial commit"')
97
+ run("git remote add jiveapps #{current_app['git_url']}")
98
+ run("git push jiveapps master")
99
+ end
100
+ end
101
+
102
+ def mute_stdout
103
+ orig_stdout = $stdout
104
+
105
+ # redirect stdout to /dev/null
106
+ $stdout = File.new('/dev/null', 'w')
107
+
108
+ yield
109
+
110
+ # restore stdout
111
+ $stdout = orig_stdout
112
+ end
113
+
114
+ def register_app
115
+ return unless current_app
116
+ debug "Registering app."
117
+
118
+ @current_app = jiveapps.register(app_name)
119
+ end
120
+
121
+ def create_notify_user
122
+ return unless current_app
123
+ debug "Notifying user."
124
+
125
+ display ""
126
+ display ""
127
+ display ""
128
+ display "Congratulations, you have created a new Jive App!"
129
+ display "================================================="
130
+ display "Git URL: #{current_app['git_url']}"
131
+ display "App URL: #{current_app['app_url']}"
132
+ display "Sandbox Canvas URL: #{current_app['sandbox_canvas_url']}"
133
+ display "Sandbox Dashboard URL: #{current_app['sandbox_dashboard_url']}"
134
+ display "OAuth Consumer Key: #{current_app['oauth_consumer_key']}"
135
+ display "OAuth Consumer Secret: #{current_app['oauth_consumer_secret']}"
136
+ end
137
+
138
+ def app_name
139
+ args.first
140
+ end
141
+
142
+ def run(command)
143
+ if debug_mode?
144
+ puts "DEBUG: $ #{command}"
145
+ `#{command}`
146
+ elsif running_on_windows?
147
+ `#{command}` # TODO: figure out how to silence on Windows
148
+ else
149
+ `#{command} > /dev/null 2>&1` # silent
150
+ end
151
+ end
152
+
153
+ def debug(msg)
154
+ if debug_mode?
155
+ puts "DEBUG: #{msg}"
156
+ end
157
+ end
158
+
159
+ end
160
+ end
@@ -0,0 +1,144 @@
1
+ module Jiveapps::Command
2
+ class Auth < Base
3
+ attr_accessor :credentials
4
+
5
+ def client
6
+ @client ||= init_jiveapps
7
+ end
8
+
9
+ def init_jiveapps
10
+ client = Jiveapps::Client.new(user, password, host)
11
+ client.on_warning { |msg| self.display("\n#{msg}\n\n") }
12
+ client
13
+ end
14
+
15
+ # just a stub; will raise if not authenticated
16
+ def check
17
+ client.list
18
+ end
19
+
20
+ def host
21
+ ENV['JIVEAPPS_HOST'] || Jiveapps::WEBHOST
22
+ end
23
+
24
+ def reauthorize
25
+ @credentials = ask_for_credentials
26
+ write_credentials
27
+ end
28
+
29
+ def user # :nodoc:
30
+ get_credentials
31
+ @credentials[0]
32
+ end
33
+
34
+ def password # :nodoc:
35
+ get_credentials
36
+ @credentials[1]
37
+ end
38
+
39
+ def credentials_file
40
+ "#{home_directory}/.jiveapps/credentials"
41
+ end
42
+
43
+ def get_credentials # :nodoc:
44
+ return if @credentials
45
+ unless @credentials = read_credentials
46
+ @credentials = ask_for_credentials
47
+ save_credentials
48
+ end
49
+ @credentials
50
+ end
51
+
52
+ def read_credentials
53
+ File.exists?(credentials_file) and File.read(credentials_file).split("\n")
54
+ end
55
+
56
+ def echo_off
57
+ system "stty -echo"
58
+ end
59
+
60
+ def echo_on
61
+ system "stty echo"
62
+ end
63
+
64
+ def ask_for_credentials
65
+ puts "Enter your Jiveapps credentials."
66
+
67
+ print "Username: "
68
+ user = ask
69
+
70
+ print "Password: "
71
+ password = running_on_windows? ? ask_for_password_on_windows : ask_for_password
72
+
73
+ [ user, password ]
74
+ end
75
+
76
+ def ask_for_password_on_windows
77
+ require "Win32API"
78
+ char = nil
79
+ password = ''
80
+
81
+ while char = Win32API.new("crtdll", "_getch", [ ], "L").Call do
82
+ break if char == 10 || char == 13 # received carriage return or newline
83
+ if char == 127 || char == 8 # backspace and delete
84
+ password.slice!(-1, 1)
85
+ else
86
+ # windows might throw a -1 at us so make sure to handle RangeError
87
+ (password << char.chr) rescue RangeError
88
+ end
89
+ end
90
+ puts
91
+ return password
92
+ end
93
+
94
+ def ask_for_password
95
+ echo_off
96
+ password = ask
97
+ puts
98
+ echo_on
99
+ return password
100
+ end
101
+
102
+ def save_credentials
103
+ begin
104
+ write_credentials
105
+ command = args.any? { |a| a == '--ignore-keys' } ? 'auth:check' : 'keys:add'
106
+ Jiveapps::Command.run_internal(command, args)
107
+ rescue RestClient::Unauthorized => e
108
+ delete_credentials
109
+ raise e unless retry_login?
110
+
111
+ display "\nAuthentication failed"
112
+ @credentials = ask_for_credentials
113
+ @client = init_jiveapps
114
+ retry
115
+ rescue Exception => e
116
+ delete_credentials
117
+ raise e
118
+ end
119
+ end
120
+
121
+ def retry_login?
122
+ @login_attempts ||= 0
123
+ @login_attempts += 1
124
+ @login_attempts < 3
125
+ end
126
+
127
+ def write_credentials
128
+ FileUtils.mkdir_p(File.dirname(credentials_file))
129
+ File.open(credentials_file, 'w') do |f|
130
+ f.puts self.credentials
131
+ end
132
+ set_credentials_permissions
133
+ end
134
+
135
+ def set_credentials_permissions
136
+ FileUtils.chmod 0700, File.dirname(credentials_file)
137
+ FileUtils.chmod 0600, credentials_file
138
+ end
139
+
140
+ def delete_credentials
141
+ FileUtils.rm_f(credentials_file)
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,19 @@
1
+ module Jiveapps::Command
2
+ class Base
3
+ include Jiveapps::Helpers
4
+
5
+ attr_accessor :args
6
+ def initialize(args)
7
+ @args = args
8
+ end
9
+
10
+ def ask
11
+ gets.strip
12
+ end
13
+
14
+ def jiveapps
15
+ @jiveapps ||= Jiveapps::Command.run_internal('auth:client', args)
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,34 @@
1
+ module Jiveapps::Command
2
+ class Help < Base
3
+
4
+ def index
5
+ puts <<-eos
6
+ === Summary
7
+ The "jiveapps" program is a command line tool for building and hosting Jive App front-ends.
8
+
9
+ === General Commands
10
+
11
+ help # show this usage
12
+
13
+ list # list your apps
14
+ create <name> # create a new app
15
+
16
+ keys # show your user's public keys
17
+ keys:add [<path to keyfile>] # add a public key. optionally include path
18
+ keys:remove <keyname> # remove a key by name (user@host)
19
+
20
+ === Simple Workflow Example:
21
+
22
+ $ jiveapps create myapp # create a new app named "myapp"
23
+ $ cd myapp # switch into app's directory
24
+
25
+ ... develop your app ...
26
+
27
+ $ git add . # stage all files for commit
28
+ $ git commit -m "some updates to my app" # commit to your local git repository
29
+ $ git push jiveapps master # push updates to jive
30
+ eos
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,83 @@
1
+ module Jiveapps::Command
2
+ class Keys < Base
3
+
4
+ # Lists uploaded SSH keys
5
+ def list
6
+ long = args.any? { |a| a == '--long' }
7
+ ssh_keys = jiveapps.keys
8
+ if ssh_keys.empty?
9
+ display "No keys for #{jiveapps.user}"
10
+ else
11
+ display "=== #{ssh_keys.size} key#{'s' if ssh_keys.size > 1} for #{jiveapps.user}"
12
+ ssh_keys.each do |ssh_key|
13
+ display long ? ssh_key['key'].strip : format_key_for_display(ssh_key['key'])
14
+ end
15
+ end
16
+ end
17
+ alias :index :list
18
+
19
+ # Uploads an SSH Key
20
+ # - args.first can either be a path to a key file or be nil. if nil, looks in default paths
21
+ def add
22
+ keyfile = find_key(args.first)
23
+ key = File.read(keyfile)
24
+
25
+ display "Uploading ssh public key #{keyfile}"
26
+ jiveapps.add_key(key)
27
+ end
28
+
29
+ # Remove an SSH key
30
+ # - args.first must be the name of the key
31
+ def remove
32
+ if args.first == nil
33
+ display "No key specified. Please specify key to remove, for example:\n$ jiveapps keys:remove name@host"
34
+ return
35
+ end
36
+ begin
37
+ jiveapps.remove_key(args.first)
38
+ display "Key #{args.first} removed."
39
+ rescue RestClient::ResourceNotFound
40
+ display "Key #{args.first} not found."
41
+ end
42
+ end
43
+
44
+ # Check to see if this machine's SSH key (or the key passed in) has been registered with Jiveapps
45
+ def check
46
+ keyfile = find_key(args.first)
47
+ key = File.read(keyfile)
48
+ key_name = key.strip.split(/\s+/).last
49
+
50
+ uploaded_key_names = jiveapps.keys.map{|key| key['name']}
51
+
52
+ if uploaded_key_names.include?(key_name)
53
+ display "This machine's SSH key \"#{key_name}\" has been registered with Jive Apps."
54
+ else
55
+ display "This machine's SSH key \"#{key_name}\" has not been registered with Jive Apps."
56
+ end
57
+ end
58
+
59
+ private
60
+ # Finds a key in the specified path or in the default locations (~/.ssh/id_(r|d)sa.pub)
61
+ def find_key(path=nil)
62
+ if !path.nil? && path.length > 0
63
+ return path if File.exists? path
64
+ raise CommandFailed, "No ssh public key found in #{path}."
65
+ else
66
+ %w(rsa dsa).each do |key_type|
67
+ keyfile = "#{home_directory}/.ssh/id_#{key_type}.pub"
68
+ return keyfile if File.exists? keyfile
69
+ end
70
+ raise CommandFailed, "No ssh public key found in #{home_directory}/.ssh/id_[rd]sa.pub. You may want to specify the full path to the keyfile or generate it with this command: ssh-keygen -t rsa"
71
+ end
72
+ end
73
+
74
+ # Formats an SSH key for display by trimming out the middle
75
+ # Example Output:
76
+ # ssh-rsa AAAAB3NzaC...Fyoke4MQ== pablo@jive
77
+ def format_key_for_display(key)
78
+ type, hex, local = key.strip.split(/\s/)
79
+ [type, hex[0,10] + '...' + hex[-10,10], local].join(' ')
80
+ end
81
+
82
+ end
83
+ end