shelly 0.0.16 → 0.0.17
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.
- data/lib/core_ext/hash.rb +17 -0
- data/lib/shelly.rb +4 -0
- data/lib/shelly/app.rb +6 -6
- data/lib/shelly/cli/main.rb +37 -44
- data/lib/shelly/cli/users.rb +18 -8
- data/lib/shelly/client.rb +21 -1
- data/lib/shelly/cloudfile.rb +45 -0
- data/lib/shelly/helpers.rb +11 -0
- data/lib/shelly/user.rb +6 -1
- data/lib/shelly/version.rb +1 -1
- data/lib/thor/options.rb +12 -0
- data/spec/shelly/app_spec.rb +46 -20
- data/spec/shelly/cli/main_spec.rb +58 -47
- data/spec/shelly/cli/users_spec.rb +62 -10
- data/spec/shelly/client_spec.rb +37 -4
- data/spec/shelly/cloudfile_spec.rb +38 -0
- data/spec/shelly/user_spec.rb +9 -1
- metadata +31 -26
@@ -0,0 +1,17 @@
|
|
1
|
+
class Hash
|
2
|
+
def deep_stringify_keys
|
3
|
+
new_hash = {}
|
4
|
+
self.each do |key, value|
|
5
|
+
new_hash.merge!(key.to_s => (value.is_a?(Hash) ? value.deep_stringify_keys : value))
|
6
|
+
end
|
7
|
+
new_hash
|
8
|
+
end
|
9
|
+
|
10
|
+
def deep_symbolize_keys
|
11
|
+
new_hash = {}
|
12
|
+
self.each do |key, value|
|
13
|
+
new_hash.merge!(key.to_sym => (value.is_a?(Hash) ? value.deep_symbolize_keys : value))
|
14
|
+
end
|
15
|
+
new_hash
|
16
|
+
end
|
17
|
+
end
|
data/lib/shelly.rb
CHANGED
@@ -1,12 +1,16 @@
|
|
1
1
|
require "rubygems"
|
2
2
|
require "thor"
|
3
3
|
require "core_ext/object"
|
4
|
+
require "core_ext/hash"
|
4
5
|
require "shelly/helpers"
|
5
6
|
require "shelly/base"
|
7
|
+
require "thor/options"
|
6
8
|
|
7
9
|
module Shelly
|
8
10
|
autoload :App, "shelly/app"
|
11
|
+
autoload :Cloudfile, "shelly/cloudfile"
|
9
12
|
autoload :Client, "shelly/client"
|
10
13
|
autoload :User, "shelly/user"
|
11
14
|
autoload :VERSION, "shelly/version"
|
12
15
|
end
|
16
|
+
|
data/lib/shelly/app.rb
CHANGED
@@ -4,7 +4,7 @@ require 'launchy'
|
|
4
4
|
module Shelly
|
5
5
|
class App < Base
|
6
6
|
DATABASE_KINDS = %w(postgresql mongodb redis none)
|
7
|
-
attr_accessor :
|
7
|
+
attr_accessor :code_name, :databases, :ruby_version, :environment, :git_url, :domains
|
8
8
|
|
9
9
|
def initialize
|
10
10
|
@ruby_version = "MRI-1.9.2"
|
@@ -12,14 +12,14 @@ module Shelly
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def add_git_remote
|
15
|
-
system("git remote rm
|
16
|
-
system("git remote add
|
15
|
+
system("git remote rm production &> /dev/null")
|
16
|
+
system("git remote add production #{git_url}")
|
17
17
|
end
|
18
18
|
|
19
19
|
def generate_cloudfile
|
20
20
|
@email = current_user.email
|
21
21
|
@databases = databases
|
22
|
-
@domains = domains
|
22
|
+
@domains = domains
|
23
23
|
template = File.read(cloudfile_template_path)
|
24
24
|
cloudfile = ERB.new(template, 0, "%<>-")
|
25
25
|
cloudfile.result(binding)
|
@@ -35,10 +35,11 @@ module Shelly
|
|
35
35
|
:code_name => code_name,
|
36
36
|
:environment => environment,
|
37
37
|
:ruby_version => ruby_version,
|
38
|
-
:domain_name =>
|
38
|
+
:domain_name => domains
|
39
39
|
}
|
40
40
|
response = shelly.create_app(attributes)
|
41
41
|
self.git_url = response["git_url"]
|
42
|
+
self.domains = response["domain_name"].split if domains.nil?
|
42
43
|
end
|
43
44
|
|
44
45
|
def create_cloudfile
|
@@ -68,4 +69,3 @@ module Shelly
|
|
68
69
|
end
|
69
70
|
end
|
70
71
|
end
|
71
|
-
|
data/lib/shelly/cli/main.rb
CHANGED
@@ -8,6 +8,7 @@ module Shelly
|
|
8
8
|
include Thor::Actions
|
9
9
|
include Helpers
|
10
10
|
register(Users, "users", "users <command>", "Manages users using this app")
|
11
|
+
check_unknown_options!
|
11
12
|
|
12
13
|
map %w(-v --version) => :version
|
13
14
|
desc "version", "Displays shelly version"
|
@@ -30,15 +31,16 @@ module Shelly
|
|
30
31
|
say "Check you mailbox for email address confirmation"
|
31
32
|
rescue Client::APIError => e
|
32
33
|
if e.validation?
|
33
|
-
e.
|
34
|
-
say_error "#{error.first} #{error.last}", :with_exit => false
|
35
|
-
end
|
34
|
+
e.each_error { |error| say_error "#{error}", :with_exit => false }
|
36
35
|
exit 1
|
37
36
|
end
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
37
|
+
rescue RestClient::Conflict
|
38
|
+
say_error "User with your ssh key already exists.", :with_exit => false
|
39
|
+
say_error "You can login using: shelly login [EMAIL]", :with_exit => false
|
40
|
+
exit 1
|
41
|
+
rescue Errno::ENOENT => e
|
42
|
+
say_error e, :with_exit => false
|
43
|
+
say_error "Use ssh-keygen to generate ssh key pair"
|
42
44
|
end
|
43
45
|
|
44
46
|
desc "login [EMAIL]", "Logins user to Shelly Cloud"
|
@@ -52,40 +54,38 @@ module Shelly
|
|
52
54
|
user.apps.each do |app|
|
53
55
|
say " #{app["code_name"]}"
|
54
56
|
end
|
55
|
-
rescue RestClient::Unauthorized
|
56
|
-
say_error "Wrong email or password", :with_exit => false
|
57
|
-
say_error "You can reset password by using link:", :with_exit => false
|
58
|
-
say_error "https://admin.winniecloud.com/users/password/new", :with_exit => false
|
59
|
-
exit 1
|
60
57
|
rescue Client::APIError => e
|
61
58
|
if e.validation?
|
62
|
-
e.
|
59
|
+
e.each_error { |error| say_error "#{error}", :with_exit => false }
|
60
|
+
exit 1
|
61
|
+
end
|
62
|
+
if e.unauthorized?
|
63
|
+
say_error "Wrong email or password", :with_exit => false
|
64
|
+
say_error "You can reset password by using link:", :with_exit => false
|
65
|
+
say_error "#{e.url}", :with_exit => false
|
63
66
|
exit 1
|
64
67
|
end
|
65
68
|
end
|
66
69
|
|
67
|
-
method_option
|
70
|
+
method_option "code-name", :type => :string, :aliases => "-c",
|
68
71
|
:desc => "Unique code_name of your application"
|
69
|
-
method_option :environment, :type => :string, :aliases => "-e",
|
70
|
-
:desc => "Environment that your application will be running"
|
71
72
|
method_option :databases, :type => :array, :aliases => "-d",
|
72
73
|
:banner => "#{Shelly::App::DATABASE_KINDS.join(' ')}",
|
73
74
|
:desc => "Array of databases of your choice"
|
74
75
|
method_option :domains, :type => :array,
|
75
|
-
:banner => "
|
76
|
+
:banner => "CODE-NAME.shellyapp.com YOUR-DOMAIN.com",
|
76
77
|
:desc => "Array of your domains"
|
77
78
|
desc "add", "Adds new application to Shelly Cloud"
|
78
79
|
def add
|
79
80
|
say_error "Must be run inside your project git repository" unless App.inside_git_repository?
|
80
81
|
check_options(options)
|
81
82
|
@app = Shelly::App.new
|
82
|
-
@app.
|
83
|
-
@app.code_name = options["code_name"] || ask_for_code_name
|
83
|
+
@app.code_name = options["code-name"] || ask_for_code_name
|
84
84
|
@app.databases = options["databases"] || ask_for_databases
|
85
85
|
@app.domains = options["domains"]
|
86
86
|
@app.create
|
87
87
|
|
88
|
-
say "Adding remote
|
88
|
+
say "Adding remote production #{@app.git_url}", :green
|
89
89
|
@app.add_git_remote
|
90
90
|
|
91
91
|
say "Creating Cloudfile", :green
|
@@ -96,22 +96,28 @@ module Shelly
|
|
96
96
|
|
97
97
|
info_adding_cloudfile_to_repository
|
98
98
|
info_deploying_to_shellycloud
|
99
|
+
|
99
100
|
rescue Client::APIError => e
|
100
101
|
if e.validation?
|
101
|
-
e.
|
102
|
-
|
102
|
+
e.each_error { |error| say_error "#{error}", :with_exit => false }
|
103
|
+
say_new_line
|
104
|
+
say_error "Fix erros in the below command and type it again to create your application" , :with_exit => false
|
105
|
+
say_error "shelly add --code-name=#{@app.code_name} --databases=#{@app.databases.join} --domains=#{@app.code_name}.shellyapp.com"
|
103
106
|
end
|
104
107
|
end
|
105
108
|
|
106
109
|
# FIXME: move to helpers
|
107
110
|
no_tasks do
|
108
111
|
def check_options(options)
|
109
|
-
unless
|
110
|
-
options.
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
112
|
+
unless options.empty?
|
113
|
+
options["domains"].map! {|domain| domain.gsub(",", "") } if options["domains"]
|
114
|
+
options["databases"].map! {|kind| kind.gsub(",", "") } if options["databases"]
|
115
|
+
unless ["code-name", "databases", "domains"].all? do |option|
|
116
|
+
options.include?(option.to_s) && options[option.to_s] != option.to_s
|
117
|
+
end && valid_databases?(options["databases"])
|
118
|
+
say_error "Try 'shelly help add' for more information"
|
119
|
+
end
|
120
|
+
end
|
115
121
|
end
|
116
122
|
|
117
123
|
def valid_databases?(databases)
|
@@ -119,14 +125,6 @@ module Shelly
|
|
119
125
|
databases.all? { |kind| kinds.include?(kind) }
|
120
126
|
end
|
121
127
|
|
122
|
-
def ask_for_email
|
123
|
-
email_question = User.guess_email.blank? ? "Email:" : "Email (#{User.guess_email} - default):"
|
124
|
-
email = ask(email_question)
|
125
|
-
email = email.blank? ? User.guess_email : email
|
126
|
-
return email if email.present?
|
127
|
-
say_error "Email can't be blank, please try again"
|
128
|
-
end
|
129
|
-
|
130
128
|
def ask_for_password(options = {})
|
131
129
|
options = {:with_confirmation => true}.merge(options)
|
132
130
|
loop do
|
@@ -146,13 +144,8 @@ module Shelly
|
|
146
144
|
end
|
147
145
|
end
|
148
146
|
|
149
|
-
def ask_for_purpose
|
150
|
-
purpose = ask("How will you use this system (production - default,staging):")
|
151
|
-
purpose.blank? ? "production" : purpose
|
152
|
-
end
|
153
|
-
|
154
147
|
def ask_for_code_name
|
155
|
-
default_code_name = "#{Shelly::App.guess_code_name}
|
148
|
+
default_code_name = "#{Shelly::App.guess_code_name}-production"
|
156
149
|
code_name = ask("Application code name (#{default_code_name} - default):")
|
157
150
|
code_name.blank? ? default_code_name : code_name
|
158
151
|
end
|
@@ -184,8 +177,8 @@ module Shelly
|
|
184
177
|
say ' git commit -m "Application added to Shelly Cloud"'
|
185
178
|
say " git push"
|
186
179
|
say_new_line
|
187
|
-
say "Deploy to
|
188
|
-
say " git push
|
180
|
+
say "Deploy to production using:", :green
|
181
|
+
say " git push production master"
|
189
182
|
say_new_line
|
190
183
|
end
|
191
184
|
end
|
data/lib/shelly/cli/users.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require "yaml"
|
2
2
|
require "shelly/user"
|
3
|
+
require "shelly/cloudfile"
|
3
4
|
|
4
5
|
module Shelly
|
5
6
|
module CLI
|
@@ -10,20 +11,29 @@ module Shelly
|
|
10
11
|
desc "list", "List users who have access to current application"
|
11
12
|
def list
|
12
13
|
say_error "Must be run inside your project git repository" unless App.inside_git_repository?
|
13
|
-
|
14
|
-
@app
|
15
|
-
|
16
|
-
|
17
|
-
say "Cloud #{app['code_name']}:"
|
18
|
-
app['users'].each do |user|
|
19
|
-
say " #{user['email']} (#{user['name']})"
|
20
|
-
end
|
14
|
+
@cloudfile = Shelly::Cloudfile.new
|
15
|
+
@cloudfile.fetch_users.each do |app, users|
|
16
|
+
say "Cloud #{app}:"
|
17
|
+
users.each { |user| say " #{user}" }
|
21
18
|
end
|
22
19
|
rescue Client::APIError => e
|
23
20
|
say e.message
|
24
21
|
exit 1
|
25
22
|
end
|
26
23
|
|
24
|
+
desc "add [EMAIL]", "Add new developer to applications defined in Cloudfile"
|
25
|
+
def add(email = nil)
|
26
|
+
say_error "Must be run inside your project git repository" unless App.inside_git_repository?
|
27
|
+
user_email = email || ask_for_email({:guess_email => false})
|
28
|
+
@cloudfile = Shelly::Cloudfile.new
|
29
|
+
@user = Shelly::User.new
|
30
|
+
@user.send_invitation(@cloudfile.clouds, user_email)
|
31
|
+
say "Sending invitation to #{user_email}"
|
32
|
+
rescue Client::APIError => e
|
33
|
+
say e.message
|
34
|
+
exit 1
|
35
|
+
end
|
36
|
+
|
27
37
|
end
|
28
38
|
end
|
29
39
|
end
|
data/lib/shelly/client.rb
CHANGED
@@ -16,9 +16,23 @@ module Shelly
|
|
16
16
|
@response["errors"]
|
17
17
|
end
|
18
18
|
|
19
|
+
def url
|
20
|
+
@response["url"]
|
21
|
+
end
|
22
|
+
|
19
23
|
def validation?
|
20
24
|
message == "Validation Failed"
|
21
25
|
end
|
26
|
+
|
27
|
+
def unauthorized?
|
28
|
+
message == "Unauthorized"
|
29
|
+
end
|
30
|
+
|
31
|
+
def each_error
|
32
|
+
@response["errors"].each do |index,message|
|
33
|
+
yield index.gsub('_',' ').capitalize + " " + message
|
34
|
+
end
|
35
|
+
end
|
22
36
|
end
|
23
37
|
|
24
38
|
def initialize(email = nil, password = nil)
|
@@ -42,6 +56,12 @@ module Shelly
|
|
42
56
|
get("/token")
|
43
57
|
end
|
44
58
|
|
59
|
+
def send_invitation(apps, email)
|
60
|
+
apps.map do |app|
|
61
|
+
post("/apps/#{app}/collaborations", :email => email)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
45
65
|
def create_app(attributes)
|
46
66
|
post("/apps", :app => attributes)
|
47
67
|
end
|
@@ -102,7 +122,7 @@ module Shelly
|
|
102
122
|
end
|
103
123
|
|
104
124
|
def process_response(response)
|
105
|
-
if [404, 422, 500].include?(response.code)
|
125
|
+
if [401, 404, 422, 500].include?(response.code)
|
106
126
|
raise APIError.new(response.body)
|
107
127
|
end
|
108
128
|
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Shelly
|
2
|
+
class Cloudfile < Base
|
3
|
+
attr_accessor :content
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
open if File.exists?(path)
|
7
|
+
end
|
8
|
+
|
9
|
+
def path
|
10
|
+
File.join(Dir.pwd, "Cloudfile")
|
11
|
+
end
|
12
|
+
|
13
|
+
def open
|
14
|
+
@content = YAML.load(File.open(path))
|
15
|
+
end
|
16
|
+
|
17
|
+
def write(hash)
|
18
|
+
@content = hash
|
19
|
+
File.open(path, "w") do |f|
|
20
|
+
f.write(yaml(hash))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def clouds
|
25
|
+
@content.keys.sort
|
26
|
+
end
|
27
|
+
|
28
|
+
def yaml(hash)
|
29
|
+
string = hash.deep_stringify_keys.to_yaml
|
30
|
+
# FIXME: check if it possible to remove sub("---", "") by passing options to_yaml
|
31
|
+
string.sub("---","").split("\n").map(&:rstrip).join("\n").strip
|
32
|
+
end
|
33
|
+
|
34
|
+
def fetch_users
|
35
|
+
response = shelly.app_users(clouds)
|
36
|
+
response.inject({}) do |result, app|
|
37
|
+
result[app['code_name']] = app['users'].map do |user|
|
38
|
+
"#{user['email']} (#{user['name']})"
|
39
|
+
end
|
40
|
+
result
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
data/lib/shelly/helpers.rb
CHANGED
@@ -16,5 +16,16 @@ module Shelly
|
|
16
16
|
say message, :red
|
17
17
|
exit 1 if options[:with_exit]
|
18
18
|
end
|
19
|
+
|
20
|
+
def ask_for_email(options = {})
|
21
|
+
options = {:guess_email => true}.merge(options)
|
22
|
+
email_question = options[:guess_email] && !User.guess_email.blank? ? "Email (#{User.guess_email} - default):" : "Email:"
|
23
|
+
email = ask(email_question)
|
24
|
+
email = email.blank? ? User.guess_email : email
|
25
|
+
return email if email.present?
|
26
|
+
say_error "Email can't be blank, please try again"
|
27
|
+
end
|
28
|
+
|
19
29
|
end
|
20
30
|
end
|
31
|
+
|
data/lib/shelly/user.rb
CHANGED
@@ -29,6 +29,10 @@ module Shelly
|
|
29
29
|
shelly.token["token"]
|
30
30
|
end
|
31
31
|
|
32
|
+
def send_invitation(apps, email)
|
33
|
+
shelly.send_invitation(apps, email)
|
34
|
+
end
|
35
|
+
|
32
36
|
def load_credentials
|
33
37
|
return unless credentials_exists?
|
34
38
|
@email, @password = File.read(credentials_path).split("\n")
|
@@ -47,7 +51,7 @@ module Shelly
|
|
47
51
|
def ssh_key_path
|
48
52
|
File.expand_path("~/.ssh/id_rsa.pub")
|
49
53
|
end
|
50
|
-
|
54
|
+
|
51
55
|
def ssh_key_registered?
|
52
56
|
ssh_key = File.read(ssh_key_path).strip
|
53
57
|
shelly.ssh_key_available?(ssh_key)
|
@@ -81,3 +85,4 @@ module Shelly
|
|
81
85
|
end
|
82
86
|
end
|
83
87
|
end
|
88
|
+
|
data/lib/shelly/version.rb
CHANGED
data/lib/thor/options.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
class Thor
|
2
|
+
class Options < Arguments
|
3
|
+
|
4
|
+
def check_unknown!
|
5
|
+
raise UnknownArgumentError, "shelly: unrecognized option '#{@unknown.join(', ')}'\n" +
|
6
|
+
"Usage: shelly [COMMAND]... [OPTIONS]\n" +
|
7
|
+
"Try 'shelly --help' for more information" unless @unknown.empty?
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
data/spec/shelly/app_spec.rb
CHANGED
@@ -8,7 +8,6 @@ describe Shelly::App do
|
|
8
8
|
@client = mock(:api_url => "https://api.example.com", :shellyapp_url => "http://shellyapp.example.com")
|
9
9
|
Shelly::Client.stub(:new).and_return(@client)
|
10
10
|
@app = Shelly::App.new
|
11
|
-
@app.purpose = "staging"
|
12
11
|
@app.code_name = "foo-staging"
|
13
12
|
end
|
14
13
|
|
@@ -42,12 +41,12 @@ describe Shelly::App do
|
|
42
41
|
end
|
43
42
|
|
44
43
|
it "should try to remove existing git remote" do
|
45
|
-
@app.should_receive(:system).with("git remote rm
|
44
|
+
@app.should_receive(:system).with("git remote rm production &> /dev/null")
|
46
45
|
@app.add_git_remote
|
47
46
|
end
|
48
47
|
|
49
48
|
it "should add git remote with proper name and git repository" do
|
50
|
-
@app.should_receive(:system).with("git remote add
|
49
|
+
@app.should_receive(:system).with("git remote add production git@git.shellycloud.com:foo-staging.git")
|
51
50
|
@app.add_git_remote
|
52
51
|
end
|
53
52
|
end
|
@@ -122,25 +121,52 @@ config
|
|
122
121
|
end
|
123
122
|
|
124
123
|
describe "#create" do
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
124
|
+
context "without providing domain" do
|
125
|
+
it "should create the app on shelly cloud via API client" do
|
126
|
+
@app.code_name = "fooo"
|
127
|
+
attributes = {
|
128
|
+
:code_name => "fooo",
|
129
|
+
:name => "fooo",
|
130
|
+
:environment => "production",
|
131
|
+
:ruby_version => "MRI-1.9.2",
|
132
|
+
:domain_name => nil
|
133
|
+
}
|
134
|
+
@client.should_receive(:create_app).with(attributes).and_return("git_url" => "git@git.shellycloud.com:fooo.git",
|
135
|
+
"domain_name" => "fooo.shellyapp.com")
|
136
|
+
@app.create
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should assign returned git_url and domain" do
|
140
|
+
@client.stub(:create_app).and_return("git_url" => "git@git.example.com:fooo.git",
|
141
|
+
"domain_name" => "fooo.shellyapp.com")
|
142
|
+
@app.create
|
143
|
+
@app.git_url.should == "git@git.example.com:fooo.git"
|
144
|
+
@app.domains.should == ["fooo.shellyapp.com"]
|
145
|
+
end
|
137
146
|
end
|
138
147
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
148
|
+
context "with providing domain" do
|
149
|
+
it "should create the app on shelly cloud via API client" do
|
150
|
+
@app.code_name = "boo"
|
151
|
+
@app.domains = ["boo.shellyapp.com", "boo.example.com"]
|
152
|
+
attributes = {
|
153
|
+
:code_name => "boo",
|
154
|
+
:name => "boo",
|
155
|
+
:environment => "production",
|
156
|
+
:ruby_version => "MRI-1.9.2",
|
157
|
+
:domain_name => ["boo.shellyapp.com", "boo.example.com"]
|
158
|
+
}
|
159
|
+
@client.should_receive(:create_app).with(attributes).and_return("git_url" => "git@git.shellycloud.com:fooo.git",
|
160
|
+
"domain_name" => "boo.shellyapp.com boo.example.com")
|
161
|
+
@app.create
|
162
|
+
end
|
163
|
+
|
164
|
+
it "should assign returned git_url and domain" do
|
165
|
+
@client.stub(:create_app).and_return("git_url" => "git@git.example.com:fooo.git",
|
166
|
+
"domain_name" => "boo.shellyapp.com boo.example.com")
|
167
|
+
@app.create
|
168
|
+
@app.domains.should == ["boo.shellyapp.com", "boo.example.com"]
|
169
|
+
end
|
143
170
|
end
|
144
171
|
end
|
145
172
|
end
|
146
|
-
|
@@ -3,7 +3,6 @@ require "shelly/cli/main"
|
|
3
3
|
|
4
4
|
describe Shelly::CLI::Main do
|
5
5
|
before do
|
6
|
-
ENV['SHELLY_GIT_HOST'] = nil
|
7
6
|
FileUtils.stub(:chmod)
|
8
7
|
@main = Shelly::CLI::Main.new
|
9
8
|
@client = mock
|
@@ -45,6 +44,16 @@ OUT
|
|
45
44
|
Shelly::User.stub(:new).and_return(@user)
|
46
45
|
end
|
47
46
|
|
47
|
+
it "should return false if ssh key don't exist on local hard drive" do
|
48
|
+
@user.stub(:ssh_key_registered?).and_raise(Errno::ENOENT)
|
49
|
+
File.exists?(File.expand_path("~/.ssh/id_rsa.pub")).should be_false
|
50
|
+
$stdout.should_receive(:puts).with("\e[31mNo such file or directory\e[0m")
|
51
|
+
$stdout.should_receive(:puts).with("\e[31mUse ssh-keygen to generate ssh key pair\e[0m")
|
52
|
+
lambda {
|
53
|
+
@main.register
|
54
|
+
}.should raise_error(SystemExit)
|
55
|
+
end
|
56
|
+
|
48
57
|
it "should check ssh key in database" do
|
49
58
|
@user.stub(:ssh_key_registered?).and_raise(RestClient::Conflict)
|
50
59
|
$stdout.should_receive(:puts).with("\e[31mUser with your ssh key already exists.\e[0m")
|
@@ -134,7 +143,7 @@ OUT
|
|
134
143
|
response = {"message" => "Validation Failed", "errors" => [["email", "has been already taken"]]}
|
135
144
|
exception = Shelly::Client::APIError.new(response.to_json)
|
136
145
|
@client.stub(:register_user).and_raise(exception)
|
137
|
-
$stdout.should_receive(:puts).with("\e[
|
146
|
+
$stdout.should_receive(:puts).with("\e[31mEmail has been already taken\e[0m")
|
138
147
|
lambda {
|
139
148
|
fake_stdin(["kate@example.com", "pass", "pass"]) do
|
140
149
|
@main.register
|
@@ -187,7 +196,8 @@ OUT
|
|
187
196
|
|
188
197
|
context "on unauthorized user" do
|
189
198
|
it "should exit with 1 and display error message" do
|
190
|
-
|
199
|
+
response = {"message" => "Unauthorized", "url" => "https://admin.winniecloud.com/users/password/new"}
|
200
|
+
exception = Shelly::Client::APIError.new(response.to_json)
|
191
201
|
@client.stub(:token).and_raise(exception)
|
192
202
|
$stdout.should_receive(:puts).with("\e[31mWrong email or password\e[0m")
|
193
203
|
$stdout.should_receive(:puts).with("\e[31mYou can reset password by using link:\e[0m")
|
@@ -219,68 +229,67 @@ OUT
|
|
219
229
|
Shelly::App.stub(:inside_git_repository?).and_return(false)
|
220
230
|
$stdout.should_receive(:puts).with("\e[31mMust be run inside your project git repository\e[0m")
|
221
231
|
lambda {
|
222
|
-
fake_stdin(["
|
232
|
+
fake_stdin(["", ""]) do
|
223
233
|
@main.add
|
224
234
|
end
|
225
235
|
}.should raise_error(SystemExit)
|
226
236
|
end
|
227
237
|
|
228
|
-
it "should ask user how he will use application" do
|
229
|
-
$stdout.should_receive(:print).with("How will you use this system (production - default,staging): ")
|
230
|
-
@app.should_receive(:purpose=).with("staging")
|
231
|
-
fake_stdin(["staging", "", ""]) do
|
232
|
-
@main.add
|
233
|
-
end
|
234
|
-
end
|
235
|
-
|
236
238
|
context "command line options" do
|
237
239
|
context "invalid params" do
|
238
240
|
it "should show help and exit if not all options are passed" do
|
239
|
-
$stdout.should_receive(:puts).with("
|
240
|
-
@main.options = {"
|
241
|
+
$stdout.should_receive(:puts).with("\e[31mTry 'shelly help add' for more information\e[0m")
|
242
|
+
@main.options = {"code-name" => "foo"}
|
241
243
|
lambda { @main.add }.should raise_error(SystemExit)
|
242
244
|
end
|
243
245
|
|
244
246
|
it "should exit if databases are not valid" do
|
245
|
-
$stdout.should_receive(:puts).with("
|
246
|
-
@main.options = {"
|
247
|
+
$stdout.should_receive(:puts).with("\e[31mTry 'shelly help add' for more information\e[0m")
|
248
|
+
@main.options = {"code-name" => "foo", "databases" => ["not existing"], "domains" => ["foo.example.com"]}
|
247
249
|
lambda { @main.add }.should raise_error(SystemExit)
|
248
250
|
end
|
249
|
-
end
|
250
251
|
|
251
|
-
|
252
|
-
|
253
|
-
@app.should_receive(:
|
254
|
-
@main.options = {"code_name" => "foo", "environment" => "production", "databases" => ["postgresql"], "domains" => ["foo.example.com"]}
|
252
|
+
it "should accept databases separated by comma" do
|
253
|
+
@main.options = {"code-name" => "foo", "databases" => ["postgresql,", "mongodb"], "domains" => ["foo.example.com"]}
|
254
|
+
@app.should_receive(:databases=).with(["postgresql", "mongodb"])
|
255
255
|
@main.add
|
256
256
|
end
|
257
|
+
|
258
|
+
it "should display which parameter was wrong" do
|
259
|
+
expected = "shelly: unrecognized option '--unknown=param'\n" +
|
260
|
+
"Usage: shelly [COMMAND]... [OPTIONS]\n" +
|
261
|
+
"Try 'shelly --help' for more information"
|
262
|
+
|
263
|
+
Open3.popen3("bin/shelly add --unknown=param") do |stdin, stdout, stderr, wait_thr|
|
264
|
+
out = stderr.read.strip
|
265
|
+
out.should == expected
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
257
269
|
end
|
258
|
-
end
|
259
270
|
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
fake_stdin(["", "", ""]) do
|
271
|
+
context "valid params" do
|
272
|
+
it "should create app on shelly cloud" do
|
273
|
+
@app.should_receive(:create)
|
274
|
+
@main.options = {"code-name" => "foo", "databases" => ["postgresql"], "domains" => ["foo.example.com"]}
|
265
275
|
@main.add
|
266
276
|
end
|
267
277
|
end
|
268
278
|
end
|
269
279
|
|
270
280
|
it "should use code name provided by user" do
|
271
|
-
$stdout.should_receive(:print).with("
|
272
|
-
$stdout.should_receive(:print).with("Application code name (foo-staging - default): ")
|
281
|
+
$stdout.should_receive(:print).with("Application code name (foo-production - default): ")
|
273
282
|
@app.should_receive(:code_name=).with("mycodename")
|
274
|
-
fake_stdin(["
|
283
|
+
fake_stdin(["mycodename", ""]) do
|
275
284
|
@main.add
|
276
285
|
end
|
277
286
|
end
|
278
287
|
|
279
288
|
context "when user provided empty code name" do
|
280
289
|
it "should use 'current_dirname-purpose' as default" do
|
281
|
-
$stdout.should_receive(:print).with("
|
282
|
-
|
283
|
-
fake_stdin(["
|
290
|
+
$stdout.should_receive(:print).with("Application code name (foo-production - default): ")
|
291
|
+
@app.should_receive(:code_name=).with("foo-production")
|
292
|
+
fake_stdin(["", ""]) do
|
284
293
|
@main.add
|
285
294
|
end
|
286
295
|
end
|
@@ -289,7 +298,7 @@ OUT
|
|
289
298
|
it "should use database provided by user (separated by comma or space)" do
|
290
299
|
$stdout.should_receive(:print).with("Which database do you want to use postgresql, mongodb, redis, none (postgresql - default): ")
|
291
300
|
@app.should_receive(:databases=).with(["postgresql", "mongodb", "redis"])
|
292
|
-
fake_stdin(["
|
301
|
+
fake_stdin(["", "postgresql ,mongodb redis"]) do
|
293
302
|
@main.add
|
294
303
|
end
|
295
304
|
end
|
@@ -297,7 +306,7 @@ OUT
|
|
297
306
|
it "should ask again for databases if unsupported kind typed" do
|
298
307
|
$stdout.should_receive(:print).with("Which database do you want to use postgresql, mongodb, redis, none (postgresql - default): ")
|
299
308
|
$stdout.should_receive(:print).with("Unknown database kind. Supported are: postgresql, mongodb, redis, none: ")
|
300
|
-
fake_stdin(["
|
309
|
+
fake_stdin(["", "postgresql,doesnt-exist", "none"]) do
|
301
310
|
@main.add
|
302
311
|
end
|
303
312
|
end
|
@@ -305,7 +314,7 @@ OUT
|
|
305
314
|
context "when user provided empty database" do
|
306
315
|
it "should use 'postgresql' database as default" do
|
307
316
|
@app.should_receive(:databases=).with(["postgresql"])
|
308
|
-
fake_stdin(["
|
317
|
+
fake_stdin(["", ""]) do
|
309
318
|
@main.add
|
310
319
|
end
|
311
320
|
end
|
@@ -313,7 +322,7 @@ OUT
|
|
313
322
|
|
314
323
|
it "should create the app on shelly cloud" do
|
315
324
|
@app.should_receive(:create)
|
316
|
-
fake_stdin(["", ""
|
325
|
+
fake_stdin(["", ""]) do
|
317
326
|
@main.add
|
318
327
|
end
|
319
328
|
end
|
@@ -322,25 +331,27 @@ OUT
|
|
322
331
|
response = {"message" => "Validation Failed", "errors" => [["code_name", "has been already taken"]]}
|
323
332
|
exception = Shelly::Client::APIError.new(response.to_json)
|
324
333
|
@app.should_receive(:create).and_raise(exception)
|
325
|
-
$stdout.should_receive(:puts).with("\e[
|
334
|
+
$stdout.should_receive(:puts).with("\e[31mCode name has been already taken\e[0m")
|
335
|
+
$stdout.should_receive(:puts).with("\e[31mFix erros in the below command and type it again to create your application\e[0m")
|
336
|
+
$stdout.should_receive(:puts).with("\e[31mshelly add --code-name=foo-production --databases=postgresql --domains=foo-production.shellyapp.com\e[0m")
|
326
337
|
lambda {
|
327
|
-
fake_stdin(["", ""
|
338
|
+
fake_stdin(["", ""]) do
|
328
339
|
@main.add
|
329
340
|
end
|
330
341
|
}.should raise_error(SystemExit)
|
331
342
|
end
|
332
343
|
|
333
344
|
it "should add git remote" do
|
334
|
-
$stdout.should_receive(:puts).with("\e[32mAdding remote
|
345
|
+
$stdout.should_receive(:puts).with("\e[32mAdding remote production git@git.shellycloud.com:foooo.git\e[0m")
|
335
346
|
@app.should_receive(:add_git_remote)
|
336
|
-
fake_stdin(["
|
347
|
+
fake_stdin(["foooo", ""]) do
|
337
348
|
@main.add
|
338
349
|
end
|
339
350
|
end
|
340
351
|
|
341
352
|
it "should create Cloudfile" do
|
342
353
|
File.exists?("/projects/foo/Cloudfile").should be_false
|
343
|
-
fake_stdin(["
|
354
|
+
fake_stdin(["foooo", ""]) do
|
344
355
|
@main.add
|
345
356
|
end
|
346
357
|
File.read("/projects/foo/Cloudfile").should == "Example Cloudfile"
|
@@ -349,7 +360,7 @@ OUT
|
|
349
360
|
it "should browser window with link to edit billing information" do
|
350
361
|
$stdout.should_receive(:puts).with("\e[32mProvide billing details. Opening browser...\e[0m")
|
351
362
|
@app.should_receive(:open_billing_page)
|
352
|
-
fake_stdin(["
|
363
|
+
fake_stdin(["foooo", ""]) do
|
353
364
|
@main.add
|
354
365
|
end
|
355
366
|
end
|
@@ -358,7 +369,7 @@ OUT
|
|
358
369
|
$stdout.should_receive(:puts).with("\e[32mProject is now configured for use with Shell Cloud:\e[0m")
|
359
370
|
$stdout.should_receive(:puts).with("\e[32mYou can review changes using\e[0m")
|
360
371
|
$stdout.should_receive(:puts).with(" git status")
|
361
|
-
fake_stdin(["
|
372
|
+
fake_stdin(["foooo", "none"]) do
|
362
373
|
@main.add
|
363
374
|
end
|
364
375
|
end
|
@@ -368,9 +379,9 @@ OUT
|
|
368
379
|
$stdout.should_receive(:puts).with(" git add .")
|
369
380
|
$stdout.should_receive(:puts).with(' git commit -m "Application added to Shelly Cloud"')
|
370
381
|
$stdout.should_receive(:puts).with(" git push")
|
371
|
-
$stdout.should_receive(:puts).with("\e[32mDeploy to
|
372
|
-
$stdout.should_receive(:puts).with(" git push
|
373
|
-
fake_stdin(["
|
382
|
+
$stdout.should_receive(:puts).with("\e[32mDeploy to production using:\e[0m")
|
383
|
+
$stdout.should_receive(:puts).with(" git push production master")
|
384
|
+
fake_stdin(["foooo", "none"]) do
|
374
385
|
@main.add
|
375
386
|
end
|
376
387
|
end
|
@@ -12,17 +12,25 @@ describe Shelly::CLI::Users do
|
|
12
12
|
$stdout.stub(:print)
|
13
13
|
end
|
14
14
|
|
15
|
+
describe "#help" do
|
16
|
+
it "should show help" do
|
17
|
+
$stdout.should_receive(:puts).with("Tasks:")
|
18
|
+
$stdout.should_receive(:puts).with(/add \[EMAIL\]\s+# Add new developer to applications defined in Cloudfile/)
|
19
|
+
$stdout.should_receive(:puts).with(/list\s+# List users who have access to current application/)
|
20
|
+
@users.help
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
15
24
|
describe "#list" do
|
16
25
|
before do
|
17
26
|
FileUtils.mkdir_p("/projects/foo")
|
18
27
|
Dir.chdir("/projects/foo")
|
19
28
|
File.open("Cloudfile", 'w') {|f| f.write("foo-staging:\nfoo-production:\n") }
|
20
|
-
@app = mock
|
21
|
-
Shelly::App.stub(:new).and_return(@app)
|
22
29
|
Shelly::App.stub(:inside_git_repository?).and_return(true)
|
23
30
|
end
|
24
31
|
|
25
32
|
it "should exit with message if command run outside git repository" do
|
33
|
+
@client.stub(:app_users).and_return(response)
|
26
34
|
Shelly::App.stub(:inside_git_repository?).and_return(false)
|
27
35
|
$stdout.should_receive(:puts).with("\e[31mMust be run inside your project git repository\e[0m")
|
28
36
|
lambda {
|
@@ -32,35 +40,79 @@ describe Shelly::CLI::Users do
|
|
32
40
|
|
33
41
|
context "on success" do
|
34
42
|
it "should receive clouds from the Cloudfile" do
|
35
|
-
@
|
43
|
+
@client.should_receive(:app_users).with(["foo-production", "foo-staging"]).
|
36
44
|
and_return(response)
|
37
45
|
@users.list
|
38
46
|
end
|
39
47
|
|
40
48
|
it "should display clouds and users" do
|
41
|
-
@
|
49
|
+
@client.stub(:app_users).and_return(response)
|
42
50
|
$stdout.should_receive(:puts).with("Cloud foo-staging:")
|
43
51
|
$stdout.should_receive(:puts).with(" user@example.com (username)")
|
44
52
|
$stdout.should_receive(:puts).with("Cloud foo-production:")
|
45
53
|
$stdout.should_receive(:puts).with(" user2@example.com (username2)")
|
46
54
|
@users.list
|
47
55
|
end
|
56
|
+
end
|
48
57
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
end
|
58
|
+
def response
|
59
|
+
[{'code_name' => 'foo-staging','users' => [{'name' => 'username','email' => 'user@example.com'}]},
|
60
|
+
{'code_name' => 'foo-production','users' => [{'name' => 'username2','email' => 'user2@example.com'}]}]
|
53
61
|
end
|
54
62
|
|
55
63
|
context "on failure" do
|
56
|
-
it "should raise an error if user does not have to any app" do
|
64
|
+
it "should raise an error if user does not have access to any app" do
|
57
65
|
response = {"message" => "You do not have access to this app"}
|
58
66
|
exception = Shelly::Client::APIError.new(response.to_json)
|
59
|
-
@
|
67
|
+
@client.stub(:app_users).and_raise(exception)
|
60
68
|
$stdout.should_receive(:puts).with("You do not have access to this app")
|
61
69
|
lambda { @users.list }.should raise_error(SystemExit)
|
62
70
|
end
|
63
71
|
end
|
64
72
|
end
|
73
|
+
|
74
|
+
describe "#add" do
|
75
|
+
before do
|
76
|
+
FileUtils.mkdir_p("/projects/foo")
|
77
|
+
Dir.chdir("/projects/foo")
|
78
|
+
File.open("Cloudfile", 'w') {|f| f.write("foo-staging:\nfoo-production:\n") }
|
79
|
+
Shelly::App.stub(:inside_git_repository?).and_return(true)
|
80
|
+
@client.stub(:token).and_return("abc")
|
81
|
+
@user = Shelly::User.new
|
82
|
+
@client.stub(:apps).and_return([{"code_name" => "abc"}, {"code_name" => "fooo"}])
|
83
|
+
Shelly::User.stub(:new).and_return(@user)
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should exit with message if command run outside git repository" do
|
87
|
+
Shelly::App.stub(:inside_git_repository?).and_return(false)
|
88
|
+
$stdout.should_receive(:puts).with("\e[31mMust be run inside your project git repository\e[0m")
|
89
|
+
lambda {
|
90
|
+
@users.add
|
91
|
+
}.should raise_error(SystemExit)
|
92
|
+
end
|
93
|
+
|
94
|
+
context "on success" do
|
95
|
+
before do
|
96
|
+
@client.should_receive(:send_invitation).with(["foo-production", "foo-staging"], "megan@example.com")
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should ask about email" do
|
100
|
+
fake_stdin(["megan@example.com"]) do
|
101
|
+
@users.add
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should receive clouds from the Cloudfile" do
|
106
|
+
@users.add("megan@example.com")
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should receive clouds from the Cloudfile" do
|
110
|
+
$stdout.should_receive(:puts).with("Sending invitation to megan@example.com")
|
111
|
+
@users.add("megan@example.com")
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
65
117
|
end
|
66
118
|
|
data/spec/shelly/client_spec.rb
CHANGED
@@ -2,7 +2,7 @@ require "spec_helper"
|
|
2
2
|
|
3
3
|
describe Shelly::Client::APIError do
|
4
4
|
before do
|
5
|
-
body = {"message" => "something went wrong", "errors" => [
|
5
|
+
body = {"message" => "something went wrong", "errors" => [["first", "foo"]], "url" => "https://foo.bar"}
|
6
6
|
@error = Shelly::Client::APIError.new(body.to_json)
|
7
7
|
end
|
8
8
|
|
@@ -10,8 +10,16 @@ describe Shelly::Client::APIError do
|
|
10
10
|
@error.message.should == "something went wrong"
|
11
11
|
end
|
12
12
|
|
13
|
-
it "should return
|
14
|
-
@error.errors.should == [
|
13
|
+
it "should return array of errors" do
|
14
|
+
@error.errors.should == [["first", "foo"]]
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should return url" do
|
18
|
+
@error.url.should == "https://foo.bar"
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should return user friendly string" do
|
22
|
+
@error.each_error{|error| error.should == "First foo"}
|
15
23
|
end
|
16
24
|
|
17
25
|
describe "#validation?" do
|
@@ -29,6 +37,22 @@ describe Shelly::Client::APIError do
|
|
29
37
|
end
|
30
38
|
end
|
31
39
|
end
|
40
|
+
|
41
|
+
describe "#unauthorized?" do
|
42
|
+
context "when error is caused by unauthorized error" do
|
43
|
+
it "should return true" do
|
44
|
+
body = {"message" => "Unauthorized"}
|
45
|
+
error = Shelly::Client::APIError.new(body.to_json)
|
46
|
+
error.should be_unauthorized
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "when error is not caused by unauthorized" do
|
51
|
+
it "should return false" do
|
52
|
+
@error.should_not be_unauthorized
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
32
56
|
end
|
33
57
|
|
34
58
|
describe Shelly::Client do
|
@@ -93,6 +117,15 @@ describe Shelly::Client do
|
|
93
117
|
end
|
94
118
|
end
|
95
119
|
|
120
|
+
describe "#send_invitation" do
|
121
|
+
it "should send post with developer's email" do
|
122
|
+
FakeWeb.register_uri(:post, @url + "/apps/staging-foo/collaborations", :body => {}.to_json)
|
123
|
+
FakeWeb.register_uri(:post, @url + "/apps/production-foo/collaborations", :body => {}.to_json)
|
124
|
+
response = @client.send_invitation(["staging-foo", "production-foo"], "megan@example.com")
|
125
|
+
response.should == [{}, {}]
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
96
129
|
describe "#update_ssh_key" do
|
97
130
|
it "should send put with give SSH key" do
|
98
131
|
@client.should_receive(:put).with("/ssh_key", {:ssh_key => "abc"})
|
@@ -169,7 +202,7 @@ describe Shelly::Client do
|
|
169
202
|
@client.get('/account')
|
170
203
|
end
|
171
204
|
|
172
|
-
%w(404 422 500).each do |code|
|
205
|
+
%w(401, 404 422 500).each do |code|
|
173
206
|
context "on #{code} response code" do
|
174
207
|
it "should raise APIError" do
|
175
208
|
@response.stub(:code).and_return(code.to_i)
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "shelly/cloudfile"
|
3
|
+
|
4
|
+
describe Shelly::Cloudfile do
|
5
|
+
before do
|
6
|
+
FileUtils.mkdir_p("/projects/foo")
|
7
|
+
Dir.chdir("/projects/foo")
|
8
|
+
@hash = {:code_name => {:code => "test"}}
|
9
|
+
@client = mock
|
10
|
+
Shelly::Client.stub(:new).and_return(@client)
|
11
|
+
@cloudfile = Shelly::Cloudfile.new
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#hash converting" do
|
15
|
+
it "should convert hash to proper string" do
|
16
|
+
@cloudfile.yaml(@hash).should == "code_name:\n code: test"
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should conver a hash to yaml format" do
|
20
|
+
@cloudfile.write(@hash)
|
21
|
+
@cloudfile.open.should == {"code_name" => {"code" => "test"}}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#fetch_users" do
|
26
|
+
it "should return array to display with clouds and users" do
|
27
|
+
@cloudfile.write(@hash)
|
28
|
+
@client.should_receive(:app_users).and_return(response)
|
29
|
+
response = @cloudfile.fetch_users
|
30
|
+
response.should == {"foo-staging" => ["user@example.com (username)"]}
|
31
|
+
end
|
32
|
+
|
33
|
+
def response
|
34
|
+
[{'code_name' => 'foo-staging','users' => [{'name' => 'username','email' => 'user@example.com'}]}]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
data/spec/shelly/user_spec.rb
CHANGED
@@ -92,6 +92,13 @@ describe Shelly::User do
|
|
92
92
|
end
|
93
93
|
end
|
94
94
|
|
95
|
+
describe "#send_invitation" do
|
96
|
+
it "should send invitation" do
|
97
|
+
@client.should_receive(:send_invitation).with(["foo-staging"], "megan@example.com")
|
98
|
+
@user.send_invitation(["foo-staging"], "megan@example.com")
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
95
102
|
describe "#ssh_key_path" do
|
96
103
|
it "should return path to public ssh key file" do
|
97
104
|
@user.ssh_key_path.should == File.expand_path("~/.ssh/id_rsa.pub")
|
@@ -105,7 +112,7 @@ describe Shelly::User do
|
|
105
112
|
@user.should_not be_ssh_key_exists
|
106
113
|
end
|
107
114
|
end
|
108
|
-
|
115
|
+
|
109
116
|
describe "#ssh_key_registered?" do
|
110
117
|
it "should read and check if ssh key exists in database" do
|
111
118
|
@client.should_receive(:ssh_key_available?).with('ssh-key AAbbcc')
|
@@ -155,3 +162,4 @@ describe Shelly::User do
|
|
155
162
|
end
|
156
163
|
end
|
157
164
|
end
|
165
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shelly
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.17
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-
|
12
|
+
date: 2011-11-03 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
|
-
requirement: &
|
16
|
+
requirement: &2156677640 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *2156677640
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rake
|
27
|
-
requirement: &
|
27
|
+
requirement: &2156677220 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *2156677220
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: guard
|
38
|
-
requirement: &
|
38
|
+
requirement: &2156676800 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *2156676800
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: guard-rspec
|
49
|
-
requirement: &
|
49
|
+
requirement: &2156676380 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: '0'
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *2156676380
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: growl_notify
|
60
|
-
requirement: &
|
60
|
+
requirement: &2156675920 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ! '>='
|
@@ -65,10 +65,10 @@ dependencies:
|
|
65
65
|
version: '0'
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *2156675920
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rb-fsevent
|
71
|
-
requirement: &
|
71
|
+
requirement: &2156675500 !ruby/object:Gem::Requirement
|
72
72
|
none: false
|
73
73
|
requirements:
|
74
74
|
- - ! '>='
|
@@ -76,10 +76,10 @@ dependencies:
|
|
76
76
|
version: '0'
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
|
-
version_requirements: *
|
79
|
+
version_requirements: *2156675500
|
80
80
|
- !ruby/object:Gem::Dependency
|
81
81
|
name: fakefs
|
82
|
-
requirement: &
|
82
|
+
requirement: &2156675080 !ruby/object:Gem::Requirement
|
83
83
|
none: false
|
84
84
|
requirements:
|
85
85
|
- - ! '>='
|
@@ -87,10 +87,10 @@ dependencies:
|
|
87
87
|
version: '0'
|
88
88
|
type: :development
|
89
89
|
prerelease: false
|
90
|
-
version_requirements: *
|
90
|
+
version_requirements: *2156675080
|
91
91
|
- !ruby/object:Gem::Dependency
|
92
92
|
name: fakeweb
|
93
|
-
requirement: &
|
93
|
+
requirement: &2156674660 !ruby/object:Gem::Requirement
|
94
94
|
none: false
|
95
95
|
requirements:
|
96
96
|
- - ! '>='
|
@@ -98,10 +98,10 @@ dependencies:
|
|
98
98
|
version: '0'
|
99
99
|
type: :development
|
100
100
|
prerelease: false
|
101
|
-
version_requirements: *
|
101
|
+
version_requirements: *2156674660
|
102
102
|
- !ruby/object:Gem::Dependency
|
103
103
|
name: thor
|
104
|
-
requirement: &
|
104
|
+
requirement: &2156674240 !ruby/object:Gem::Requirement
|
105
105
|
none: false
|
106
106
|
requirements:
|
107
107
|
- - ! '>='
|
@@ -109,10 +109,10 @@ dependencies:
|
|
109
109
|
version: '0'
|
110
110
|
type: :runtime
|
111
111
|
prerelease: false
|
112
|
-
version_requirements: *
|
112
|
+
version_requirements: *2156674240
|
113
113
|
- !ruby/object:Gem::Dependency
|
114
114
|
name: rest-client
|
115
|
-
requirement: &
|
115
|
+
requirement: &2156673780 !ruby/object:Gem::Requirement
|
116
116
|
none: false
|
117
117
|
requirements:
|
118
118
|
- - ! '>='
|
@@ -120,10 +120,10 @@ dependencies:
|
|
120
120
|
version: '0'
|
121
121
|
type: :runtime
|
122
122
|
prerelease: false
|
123
|
-
version_requirements: *
|
123
|
+
version_requirements: *2156673780
|
124
124
|
- !ruby/object:Gem::Dependency
|
125
125
|
name: json
|
126
|
-
requirement: &
|
126
|
+
requirement: &2156673340 !ruby/object:Gem::Requirement
|
127
127
|
none: false
|
128
128
|
requirements:
|
129
129
|
- - ! '>='
|
@@ -131,10 +131,10 @@ dependencies:
|
|
131
131
|
version: '0'
|
132
132
|
type: :runtime
|
133
133
|
prerelease: false
|
134
|
-
version_requirements: *
|
134
|
+
version_requirements: *2156673340
|
135
135
|
- !ruby/object:Gem::Dependency
|
136
136
|
name: launchy
|
137
|
-
requirement: &
|
137
|
+
requirement: &2156672840 !ruby/object:Gem::Requirement
|
138
138
|
none: false
|
139
139
|
requirements:
|
140
140
|
- - ! '>='
|
@@ -142,7 +142,7 @@ dependencies:
|
|
142
142
|
version: '0'
|
143
143
|
type: :runtime
|
144
144
|
prerelease: false
|
145
|
-
version_requirements: *
|
145
|
+
version_requirements: *2156672840
|
146
146
|
description: Tool for managing applications and clouds at shellycloud.com
|
147
147
|
email:
|
148
148
|
- support@shellycloud.com
|
@@ -158,6 +158,7 @@ files:
|
|
158
158
|
- README.md
|
159
159
|
- Rakefile
|
160
160
|
- bin/shelly
|
161
|
+
- lib/core_ext/hash.rb
|
161
162
|
- lib/core_ext/object.rb
|
162
163
|
- lib/shelly.rb
|
163
164
|
- lib/shelly/app.rb
|
@@ -165,10 +166,12 @@ files:
|
|
165
166
|
- lib/shelly/cli/main.rb
|
166
167
|
- lib/shelly/cli/users.rb
|
167
168
|
- lib/shelly/client.rb
|
169
|
+
- lib/shelly/cloudfile.rb
|
168
170
|
- lib/shelly/helpers.rb
|
169
171
|
- lib/shelly/templates/Cloudfile.erb
|
170
172
|
- lib/shelly/user.rb
|
171
173
|
- lib/shelly/version.rb
|
174
|
+
- lib/thor/options.rb
|
172
175
|
- shelly.gemspec
|
173
176
|
- spec/helpers.rb
|
174
177
|
- spec/input_faker.rb
|
@@ -177,6 +180,7 @@ files:
|
|
177
180
|
- spec/shelly/cli/main_spec.rb
|
178
181
|
- spec/shelly/cli/users_spec.rb
|
179
182
|
- spec/shelly/client_spec.rb
|
183
|
+
- spec/shelly/cloudfile_spec.rb
|
180
184
|
- spec/shelly/user_spec.rb
|
181
185
|
- spec/spec_helper.rb
|
182
186
|
homepage: http://shellycloud.com
|
@@ -211,5 +215,6 @@ test_files:
|
|
211
215
|
- spec/shelly/cli/main_spec.rb
|
212
216
|
- spec/shelly/cli/users_spec.rb
|
213
217
|
- spec/shelly/client_spec.rb
|
218
|
+
- spec/shelly/cloudfile_spec.rb
|
214
219
|
- spec/shelly/user_spec.rb
|
215
220
|
- spec/spec_helper.rb
|