jiveapps 0.0.3

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