engineyard 1.4.29 → 1.7.0.pre2

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.
Files changed (68) hide show
  1. data/README.rdoc +139 -4
  2. data/bin/ey +1 -7
  3. data/lib/engineyard.rb +1 -22
  4. data/lib/engineyard/cli.rb +192 -94
  5. data/lib/engineyard/cli/#recipes.rb# +32 -0
  6. data/lib/engineyard/cli/api.rb +42 -28
  7. data/lib/engineyard/cli/recipes.rb +13 -6
  8. data/lib/engineyard/cli/ui.rb +103 -42
  9. data/lib/engineyard/cli/web.rb +16 -10
  10. data/lib/engineyard/config.rb +92 -18
  11. data/lib/engineyard/deploy_config.rb +66 -0
  12. data/lib/engineyard/deploy_config/migrate.rb +125 -0
  13. data/lib/engineyard/deploy_config/ref.rb +56 -0
  14. data/lib/engineyard/error.rb +38 -78
  15. data/lib/engineyard/repo.rb +75 -27
  16. data/lib/engineyard/serverside_runner.rb +133 -0
  17. data/lib/engineyard/thor.rb +110 -18
  18. data/lib/engineyard/version.rb +1 -1
  19. data/spec/engineyard/cli/api_spec.rb +10 -16
  20. data/spec/engineyard/cli_spec.rb +0 -11
  21. data/spec/engineyard/config_spec.rb +1 -8
  22. data/spec/engineyard/deploy_config_spec.rb +203 -0
  23. data/spec/engineyard/eyrc_spec.rb +2 -0
  24. data/spec/engineyard/repo_spec.rb +57 -34
  25. data/spec/ey/deploy_spec.rb +102 -52
  26. data/spec/ey/list_environments_spec.rb +69 -14
  27. data/spec/ey/login_spec.rb +11 -7
  28. data/spec/ey/logout_spec.rb +4 -4
  29. data/spec/ey/logs_spec.rb +6 -6
  30. data/spec/ey/recipes/apply_spec.rb +1 -1
  31. data/spec/ey/recipes/download_spec.rb +1 -1
  32. data/spec/ey/recipes/upload_spec.rb +6 -6
  33. data/spec/ey/rollback_spec.rb +3 -3
  34. data/spec/ey/ssh_spec.rb +9 -9
  35. data/spec/ey/status_spec.rb +2 -2
  36. data/spec/ey/whoami_spec.rb +9 -8
  37. data/spec/spec_helper.rb +18 -15
  38. data/spec/support/{fake_awsm.rb → git_repos.rb} +0 -14
  39. data/spec/support/helpers.rb +84 -28
  40. data/spec/support/matchers.rb +0 -16
  41. data/spec/support/shared_behavior.rb +83 -103
  42. metadata +65 -51
  43. data/lib/engineyard/api.rb +0 -117
  44. data/lib/engineyard/collection.rb +0 -7
  45. data/lib/engineyard/collection/abstract.rb +0 -71
  46. data/lib/engineyard/collection/apps.rb +0 -8
  47. data/lib/engineyard/collection/environments.rb +0 -8
  48. data/lib/engineyard/model.rb +0 -12
  49. data/lib/engineyard/model/account.rb +0 -8
  50. data/lib/engineyard/model/api_struct.rb +0 -33
  51. data/lib/engineyard/model/app.rb +0 -32
  52. data/lib/engineyard/model/deployment.rb +0 -90
  53. data/lib/engineyard/model/environment.rb +0 -194
  54. data/lib/engineyard/model/instance.rb +0 -166
  55. data/lib/engineyard/model/log.rb +0 -9
  56. data/lib/engineyard/model/user.rb +0 -6
  57. data/lib/engineyard/resolver.rb +0 -134
  58. data/lib/engineyard/rest_client_ext.rb +0 -9
  59. data/lib/engineyard/ruby_ext.rb +0 -9
  60. data/spec/engineyard/api_spec.rb +0 -39
  61. data/spec/engineyard/collection/apps_spec.rb +0 -16
  62. data/spec/engineyard/collection/environments_spec.rb +0 -16
  63. data/spec/engineyard/model/api_struct_spec.rb +0 -41
  64. data/spec/engineyard/model/environment_spec.rb +0 -198
  65. data/spec/engineyard/model/instance_spec.rb +0 -27
  66. data/spec/engineyard/resolver_spec.rb +0 -112
  67. data/spec/support/fake_awsm.ru +0 -245
  68. data/spec/support/scenarios.rb +0 -417
@@ -0,0 +1,133 @@
1
+ require 'escape'
2
+ require 'net/ssh'
3
+ require 'engineyard-serverside-adapter'
4
+
5
+ module EY
6
+ class ServersideRunner
7
+ def initialize(bridge, app, environment, verbose)
8
+ @verbose = verbose || ENV['DEBUG']
9
+ @adapter = load_adapter(bridge, app, environment)
10
+ @username = environment.username
11
+ @hostname = bridge
12
+ @command = nil
13
+ end
14
+
15
+ def deploy(&block)
16
+ @command = @adapter.deploy(&block)
17
+ self
18
+ end
19
+
20
+ def rollback(&block)
21
+ @command = @adapter.rollback(&block)
22
+ self
23
+ end
24
+
25
+ def put_up_maintenance_page(&block)
26
+ @command = @adapter.enable_maintenance_page(&block)
27
+ self
28
+ end
29
+
30
+ def take_down_maintenance_page(&block)
31
+ @command = @adapter.disable_maintenance_page(&block)
32
+ self
33
+ end
34
+
35
+ def call(out, err)
36
+ raise "No command!" unless @command
37
+ @command.call do |cmd|
38
+ run cmd, out, err
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def load_adapter(bridge, app, environment)
45
+ EY::Serverside::Adapter.new("/usr/local/ey_resin/ruby/bin") do |args|
46
+ args.app = app.name
47
+ args.repo = app.repository_uri
48
+ args.instances = instances_data(environment.deploy_to_instances, bridge)
49
+ args.stack = environment.app_server_stack_name
50
+ args.framework_env = environment.framework_env
51
+ args.environment_name = environment.name
52
+ args.account_name = app.account.name
53
+ args.verbose = @verbose
54
+ end
55
+ end
56
+
57
+ # If we tell engineyard-serverside to use 'localhost', it'll run
58
+ # commands on the instance directly (#system). If we give it the
59
+ # instance's actual hostname, it'll SSH to itself.
60
+ #
61
+ # Using 'localhost' instead of its EC2 hostname speeds up
62
+ # deploys on solos and single-app-server clusters significantly.
63
+ def instances_data(instances, bridge)
64
+ instances.map do |i|
65
+ {
66
+ :hostname => i.hostname == bridge ? 'localhost' : i.hostname,
67
+ :roles => [i.role],
68
+ :name => i.name,
69
+ }
70
+ end
71
+ end
72
+
73
+ def run(remote_command, out, err)
74
+ cmd = Escape.shell_command(['bash', '-lc', remote_command])
75
+
76
+ if cmd.respond_to?(:encoding) && cmd.respond_to?(:force_encoding)
77
+ out << "Encoding: #{cmd.encoding.name}" if @verbose
78
+ cmd.force_encoding('binary')
79
+ out << " => #{cmd.encoding.name}; __ENCODING__: #{__ENCODING__.name}; LANG: #{ENV['LANG']}; LC_CTYPE: #{ENV['LC_CTYPE']}\n" if @verbose
80
+ end
81
+
82
+ out << "Running command on #{@username}@#{@hostname}.\n"
83
+ out << cmd << "\n" if @verbose
84
+
85
+ if ENV["NO_SSH"]
86
+ out << "NO_SSH is set. No output.\n"
87
+ true
88
+ else
89
+ begin
90
+ ssh(cmd, @hostname, @username, out, err)
91
+ rescue Net::SSH::AuthenticationFailed
92
+ raise EY::Error, "Authentication Failed: Please add your environment's ssh key with: ssh-add path/to/key"
93
+ end
94
+ end
95
+ end
96
+
97
+ def ssh(cmd, hostname, username, out, err)
98
+ exit_code = 1
99
+ options_for_ssh = {:paranoid => false}
100
+ options_for_ssh[:verbose] = ENV["DEBUG"].downcase.to_sym if ENV["DEBUG"]
101
+ Net::SSH.start(hostname, username, options_for_ssh) do |net_ssh|
102
+ net_ssh.open_channel do |channel|
103
+ channel.exec cmd do |_, success|
104
+ unless success
105
+ err << "Remote command execution failed"
106
+ return false
107
+ end
108
+
109
+ channel.on_data do |_, data|
110
+ out << data
111
+ end
112
+
113
+ channel.on_extended_data do |_, _, data|
114
+ err << data
115
+ end
116
+
117
+ channel.on_request("exit-status") do |_, data|
118
+ exit_code = data.read_long
119
+ end
120
+
121
+ channel.on_request("exit-signal") do |_, data|
122
+ exit_code = 255
123
+ end
124
+ end
125
+ end
126
+
127
+ net_ssh.loop
128
+ end
129
+ exit_code.zero?
130
+ end
131
+
132
+ end
133
+ end
@@ -4,33 +4,125 @@ module EY
4
4
  module UtilityMethods
5
5
  protected
6
6
  def api
7
- @api ||= EY::CLI::API.new
7
+ @api ||= EY::CLI::API.new(config.endpoint, ui)
8
+ end
9
+
10
+ def config
11
+ @config ||= EY::Config.new
12
+ end
13
+
14
+ def ui
15
+ @ui ||= load_ui
16
+ end
17
+
18
+ def load_ui
19
+ Thor::Base.shell = EY::CLI::UI
20
+ EY::CLI::UI.new
21
+ end
22
+
23
+ def in_repo?
24
+ EY::Repo.exist?
8
25
  end
9
26
 
10
27
  def repo
11
28
  @repo ||= EY::Repo.new
12
29
  end
13
30
 
14
- def fetch_environment(environment_name, account_name=nil)
15
- environment_name ||= EY.config.default_environment
16
- options = {
31
+ def serverside_runner(app_env, verbose)
32
+ ServersideRunner.new(app_env.environment.bridge!.hostname, app_env.app, app_env.environment, verbose)
33
+ end
34
+
35
+ def use_default_environment
36
+ if env = config.default_environment
37
+ ui.say "Using default environment #{config.default_environment.inspect} from ey.yml."
38
+ env
39
+ end
40
+ end
41
+
42
+ def fetch_environment(environment_name, account_name)
43
+ environment_name ||= use_default_environment
44
+ remotes = repo.remotes if in_repo?
45
+ constraints = {
17
46
  :environment_name => environment_name,
18
- :account_name => account_name
47
+ :account_name => account_name,
48
+ :remotes => remotes,
19
49
  }
20
- options.merge! :repo => repo if repo.exist?
21
- api.resolver.environment(options)
50
+
51
+ resolver = api.resolve_environments(constraints)
52
+
53
+ resolver.one_match { |match| return match }
54
+
55
+ resolver.no_matches do |errors, suggestions|
56
+ raise_no_matches(errors, suggestions)
57
+ end
58
+
59
+ resolver.many_matches do |matches|
60
+ if environment_name
61
+ message = "Multiple environments possible, please be more specific:\n\n"
62
+ matches.each do |env|
63
+ message << "\t#{env.name.ljust(25)} # ey <command> --environment='#{env.name}' --account='#{env.account.name}'\n"
64
+ end
65
+ raise EY::MultipleMatchesError.new(message)
66
+ else
67
+ raise EY::AmbiguousEnvironmentGitUriError.new(matches)
68
+ end
69
+ end
22
70
  end
23
71
 
24
- def fetch_app_and_environment(app_name = nil, environment_name = nil, account_name=nil)
25
- options = {
26
- :app_name => app_name,
72
+ def fetch_app_environment(app_name, environment_name, account_name)
73
+ environment_name ||= use_default_environment
74
+ remotes = repo.remotes if in_repo?
75
+ constraints = {
76
+ :app_name => app_name,
27
77
  :environment_name => environment_name,
28
- :account_name => account_name
78
+ :account_name => account_name,
79
+ :remotes => remotes,
29
80
  }
30
- options.merge! :repo => repo if repo.exist?
31
- api.resolver.app_and_environment(options)
81
+
82
+ if constraints.all? { |k,v| v.nil? || v.empty? || v.to_s.empty? }
83
+ raise EY::NoMatchesError.new <<-ERROR
84
+ Unable to find application without a git remote URI or app name.
85
+
86
+ Please specify --app app_name or add this application at #{config.endpoint}"
87
+ ERROR
88
+ end
89
+
90
+ resolver = api.resolve_app_environments(constraints)
91
+
92
+ resolver.one_match { |match| return match }
93
+ resolver.no_matches do |errors, suggestions|
94
+ raise_no_matches(errors, suggestions)
95
+ end
96
+ resolver.many_matches do |app_envs|
97
+ raise EY::MultipleMatchesError.new(too_many_app_environments_error(app_envs))
98
+ end
99
+ end
100
+
101
+ def raise_no_matches(errors, suggestions)
102
+ message = "We found the following suggestions:\n" if suggestions.any?
103
+
104
+ suggestions.each do |suggest|
105
+ message << " # ey <command> --account='#{suggest['account_name']}' --app='#{suggest['app_name']}' --environment='#{suggest['env_name']}'\n"
106
+ end
107
+
108
+ raise EY::NoMatchesError.new([errors,message].compact.join("\n").strip)
32
109
  end
33
110
 
111
+ def too_many_app_environments_error(app_envs)
112
+ message = "Multiple application environments possible, please be more specific:\n\n"
113
+
114
+ app_envs.group_by do |app_env|
115
+ [app_env.account_name, app_env.app_name]
116
+ end.sort_by { |k,v| k.join }.each do |(account_name, app_name), grouped_app_envs|
117
+ message << "\n"
118
+ message << account_name << "/" << app_name << "\n"
119
+ grouped_app_envs.map { |ae| ae.environment_name }.uniq.sort.each do |env_name|
120
+ message << "\t#{env_name.ljust(25)}"
121
+ message << " # ey <command> --account='#{account_name}' --app='#{app_name}' --environment='#{env_name}'\n"
122
+ end
123
+ end
124
+ message
125
+ end
34
126
  end # UtilityMethods
35
127
 
36
128
  class Thor < ::Thor
@@ -44,13 +136,13 @@ module EY
44
136
  class_eval <<-RUBY
45
137
  def help(*args)
46
138
  if args.empty?
47
- EY.ui.say "usage: #{banner_base} #{cmd} COMMAND"
48
- EY.ui.say
139
+ ui.say "usage: #{banner_base} #{cmd} COMMAND"
140
+ ui.say
49
141
  subcommands = self.class.printable_tasks.sort_by{|s| s[0] }
50
142
  subcommands.reject!{|t| t[0] =~ /#{cmd} help$/}
51
- EY.ui.print_help(subcommands)
52
- EY.ui.say self.class.send(:class_options_help, EY.ui)
53
- EY.ui.say "See #{banner_base} #{cmd} help COMMAND" +
143
+ ui.print_help(subcommands)
144
+ ui.say self.class.send(:class_options_help, ui)
145
+ ui.say "See #{banner_base} #{cmd} help COMMAND" +
54
146
  " for more information on a specific subcommand." if args.empty?
55
147
  else
56
148
  super
@@ -1,3 +1,3 @@
1
1
  module EY
2
- VERSION = '1.4.29'
2
+ VERSION = '1.7.0.pre2'
3
3
  end
@@ -2,42 +2,36 @@ require 'spec_helper'
2
2
  require 'engineyard/cli'
3
3
 
4
4
  describe EY::CLI::API do
5
- before(:all) do
6
- EY.ui = EY::CLI::UI.new
7
- end
8
-
9
- after(:all) do
10
- EY.ui = EY::UI.new
11
- end
12
-
13
5
  it "gets the api token from ~/.eyrc if possible" do
14
6
  write_eyrc({"api_token" => "asdf"})
15
- EY::CLI::API.new.should == EY::CLI::API.new("asdf")
7
+ EY::CLI::API.new('http://fake.local', EY::CLI::UI.new).token.should == "asdf"
8
+ clean_eyrc
16
9
  end
17
10
 
18
11
  it "uses the token from $ENGINEYARD_API_TOKEN if set" do
19
12
  ENV['ENGINEYARD_API_TOKEN'] = 'envtoken'
20
- EY::CLI::API.new.token.should == 'envtoken'
13
+ EY::CLI::API.new('http://fake.local', EY::CLI::UI.new).token.should == 'envtoken'
21
14
  ENV.delete('ENGINEYARD_API_TOKEN')
22
15
  end
23
16
 
24
17
  context "without saved api token" do
25
18
  before(:each) do
26
- FakeWeb.register_uri(:post, "https://cloud.engineyard.com/api/v2/authenticate", :body => %|{"api_token": "asdf"}|, :content_type => 'application/json')
19
+ clean_eyrc
20
+ FakeWeb.register_uri(:post, "http://fake.local/api/v2/authenticate", :body => %|{"api_token": "asdf"}|, :content_type => 'application/json')
27
21
 
28
22
  EY::CLI::UI::Prompter.enable_mock!
29
- EY::CLI::UI::Prompter.backend.next_answer = "my@email.example.com"
30
- EY::CLI::UI::Prompter.backend.next_answer = "secret"
23
+ EY::CLI::UI::Prompter.add_answer "my@email.example.com"
24
+ EY::CLI::UI::Prompter.add_answer "secret"
31
25
 
32
- @api = EY::CLI::API.new
26
+ @api = EY::CLI::API.new('http://fake.local', EY::CLI::UI.new)
33
27
  end
34
28
 
35
29
  it "asks you for your credentials" do
36
- EY::CLI::UI::Prompter.backend.questions.should == ["Email: ","Password: "]
30
+ EY::CLI::UI::Prompter.questions.should == ["Email: ","Password: "]
37
31
  end
38
32
 
39
33
  it "gets the api token" do
40
- @api.should == EY::CLI::API.new("asdf")
34
+ @api.token.should == "asdf"
41
35
  end
42
36
 
43
37
  it "saves the api token to ~/.eyrc" do
@@ -3,15 +3,6 @@ require 'engineyard/cli'
3
3
 
4
4
  describe EY::CLI do
5
5
 
6
- it "sets up EY.ui" do
7
- EY.instance_eval{ @ui = nil }
8
- EY.ui.should be_an(EY::UI)
9
- capture_stdout do
10
- EY::CLI.start(["help"])
11
- end
12
- EY.ui.should be_an(EY::CLI::UI)
13
- end
14
-
15
6
  it "provides help" do
16
7
  out = capture_stdout do
17
8
  EY::CLI.start(["help"])
@@ -31,8 +22,6 @@ describe EY::CLI do
31
22
  end
32
23
 
33
24
  it "provides error classes" do
34
- EY::EnvironmentError.should be
35
- EY::BranchMismatchError.should be
36
25
  EY::DeployArgumentError.should be
37
26
  end
38
27
 
@@ -23,14 +23,7 @@ describe EY::Config do
23
23
 
24
24
  it "loads the endpoint from $CLOUD_URL" do
25
25
  ENV['CLOUD_URL'] = "http://fake.local/"
26
- EY::Config.new.endpoint.should == URI.parse('http://fake.local')
27
- ENV.delete('CLOUD_URL')
28
- end
29
-
30
- it "raises on an invalid endpoint" do
31
- ENV['CLOUD_URL'] = "non/absolute"
32
- lambda { EY::Config.new.endpoint }.
33
- should raise_error(EY::Config::ConfigurationError)
26
+ EY::Config.new.endpoint.should == 'http://fake.local/'
34
27
  ENV.delete('CLOUD_URL')
35
28
  end
36
29
  end
@@ -0,0 +1,203 @@
1
+ require 'spec_helper'
2
+
3
+ describe EY::DeployConfig::Migrate do
4
+ before do
5
+ @parent = mock('parent config mock')
6
+ @parent.stub!(:set_environment_option)
7
+ @parent.stub!(:path).and_return('path')
8
+ @ui = EY::CLI::UI.new
9
+ EY::CLI::UI::Prompter.enable_mock!
10
+ @repo = mock('repo')
11
+ end
12
+
13
+ def env_config(opts=nil)
14
+ @env_config ||= EY::Config::EnvironmentConfig.new(opts, 'envname', @parent)
15
+ end
16
+
17
+ def deploy_config(cli_options)
18
+ EY::DeployConfig.new(cli_options, env_config, @repo, @ui)
19
+ end
20
+
21
+ context "inside a repository" do
22
+ context "no migrate options set (interactive)" do
23
+ it "prompts migrate and command by default" do
24
+ EY::CLI::UI::Prompter.add_answer "" # default
25
+ EY::CLI::UI::Prompter.add_answer ""
26
+ @parent.should_receive(:set_environment_option).with('envname', 'migrate', true)
27
+ @parent.should_receive(:set_environment_option).with('envname', 'migration_command', 'rake db:migrate')
28
+ dc = deploy_config({})
29
+ dc.migrate.should be_true
30
+ dc.migrate_command.should == 'rake db:migrate'
31
+ end
32
+
33
+ it "prompts migration_command if first answer is yes" do
34
+ EY::CLI::UI::Prompter.add_answer "yes" # default
35
+ EY::CLI::UI::Prompter.add_answer "ruby script/migrate"
36
+ @parent.should_receive(:set_environment_option).with('envname', 'migrate', true)
37
+ @parent.should_receive(:set_environment_option).with('envname', 'migration_command', 'ruby script/migrate')
38
+ dc = deploy_config({})
39
+ dc.migrate.should be_true
40
+ dc.migrate_command.should == 'ruby script/migrate'
41
+ end
42
+
43
+ it "doesn't prompt migration_command if first answer is no" do
44
+ EY::CLI::UI::Prompter.add_answer "no" # default
45
+ @parent.should_receive(:set_environment_option).with('envname', 'migrate', false)
46
+ dc = deploy_config({})
47
+ dc.migrate.should be_false
48
+ dc.migrate_command.should be_nil
49
+ end
50
+ end
51
+
52
+ context "with the migrate cli option" do
53
+ it "returns the default migration command when is true" do
54
+ dc = deploy_config({'migrate' => true})
55
+ dc.migrate.should be_true
56
+ dc.migrate_command.should == 'rake db:migrate'
57
+ end
58
+
59
+ it "returns false when nil" do
60
+ dc = deploy_config({'migrate' => nil})
61
+ dc.migrate.should be_false
62
+ dc.migrate_command.should be_nil
63
+ end
64
+
65
+ it "return the custom migration command when is a string" do
66
+ dc = deploy_config({'migrate' => 'foo migrate'})
67
+ dc.migrate.should be_true
68
+ dc.migrate_command.should == 'foo migrate'
69
+ end
70
+ end
71
+
72
+ context "with the migrate option in the global configuration" do
73
+ it "return the default migration command when the option is true" do
74
+ env_config('migrate' => true, 'migration_command' => 'bar migrate')
75
+ dc = deploy_config({})
76
+ dc.migrate.should be_true
77
+ dc.migrate_command.should == 'bar migrate'
78
+ end
79
+
80
+ it "return the false when migrate is false" do
81
+ env_config('migrate' => false, 'migration_command' => 'bar migrate')
82
+ dc = deploy_config({})
83
+ dc.migrate.should be_false
84
+ dc.migrate_command.should be_nil
85
+ end
86
+
87
+ it "return the default migration command when the option is true" do
88
+ env_config('migrate' => true)
89
+ dc = deploy_config({})
90
+ dc.migrate.should be_true
91
+ dc.migrate_command.should == 'rake db:migrate'
92
+ end
93
+
94
+ it "return the ey.yml migration_command when command line option --migrate is passed" do
95
+ env_config('migrate' => false, 'migration_command' => 'bar migrate')
96
+ dc = deploy_config({'migrate' => true})
97
+ dc.migrate.should be_true
98
+ dc.migrate_command.should == 'bar migrate'
99
+ end
100
+ end
101
+
102
+ describe "ref" do
103
+ it "returns the passed ref" do
104
+ deploy_config({'ref' => 'master'}).ref.should == 'master'
105
+ end
106
+
107
+ it "returns the passed force_ref" do
108
+ deploy_config({'force_ref' => 'force'}).ref.should == 'force'
109
+ end
110
+
111
+ it "returns the ref if force_ref is true" do
112
+ deploy_config({'ref' => 'master', 'force_ref' => true}).ref.should == 'master'
113
+ end
114
+
115
+ it "overrides the ref if force_ref is set to a string" do
116
+ deploy_config({'ref' => 'master', 'force_ref' => 'force'}).ref.should == 'force'
117
+ end
118
+
119
+ context "with a default branch" do
120
+ before { env_config('branch' => 'default') }
121
+
122
+ it "uses the configured default if ref is not passed" do
123
+ deploy_config({}).ref.should == 'default'
124
+ end
125
+
126
+ it "raises if a default is set and --ref is passed on the cli (and they don't match)" do
127
+ lambda { deploy_config({'ref' => 'master'}).ref }.should raise_error(EY::BranchMismatchError)
128
+ end
129
+
130
+ it "returns the default if a default is set and --ref is the same" do
131
+ deploy_config({'ref' => 'default'}).ref.should == 'default'
132
+ end
133
+
134
+ it "returns the ref if force_ref is set" do
135
+ deploy_config({'ref' => 'master', 'force_ref' => true}).ref.should == 'master'
136
+ end
137
+
138
+ it "returns the ref if force_ref is a branch" do
139
+ deploy_config({'force_ref' => 'master'}).ref.should == 'master'
140
+ end
141
+ end
142
+
143
+ context "no options, no default" do
144
+ it "uses the repo's current branch" do
145
+ @repo.should_receive(:current_branch).and_return('current')
146
+ deploy_config({}).ref.should == 'current'
147
+ end
148
+ end
149
+ end
150
+ end
151
+
152
+ context "when outside of a repo" do
153
+ describe "migrate" do
154
+ it "returns the default migration command when migrate is true" do
155
+ dc = deploy_config({'app' => 'app', 'migrate' => true})
156
+ dc.migrate.should be_true
157
+ dc.migrate_command.should == 'rake db:migrate'
158
+ end
159
+
160
+ it "returns false when nil" do
161
+ dc = deploy_config({'app' => 'app', 'migrate' => nil})
162
+ dc.migrate.should be_false
163
+ dc.migrate_command.should be_nil
164
+ end
165
+
166
+ it "return the custom migration command when is a string" do
167
+ dc = deploy_config({'app' => 'app', 'migrate' => 'foo migrate'})
168
+ dc.migrate.should be_true
169
+ dc.migrate_command.should == 'foo migrate'
170
+ end
171
+
172
+ it "raises if migrate is not passed" do
173
+ lambda { deploy_config({'app' => 'app'}).migrate }.should raise_error(EY::RefAndMigrateRequiredOutsideRepo)
174
+ end
175
+ end
176
+
177
+ describe "ref" do
178
+ it "returns the passed ref" do
179
+ dc = deploy_config({'app' => 'app', 'ref' => 'master'})
180
+ dc.ref.should == 'master'
181
+ end
182
+
183
+ it "returns the passed force_ref" do
184
+ dc = deploy_config({'app' => 'app', 'force_ref' => 'force'})
185
+ dc.ref.should == 'force'
186
+ end
187
+
188
+ it "returns the ref if force_ref is true" do
189
+ dc = deploy_config({'app' => 'app', 'ref' => 'master', 'force_ref' => true})
190
+ dc.ref.should == 'master'
191
+ end
192
+
193
+ it "overrides the ref if force_ref is set to a string" do
194
+ dc = deploy_config({'app' => 'app', 'ref' => 'master', 'force_ref' => 'force'})
195
+ dc.ref.should == 'force'
196
+ end
197
+
198
+ it "raises if ref is not passed" do
199
+ lambda { deploy_config({'app' => 'app'}).ref }.should raise_error(EY::RefAndMigrateRequiredOutsideRepo)
200
+ end
201
+ end
202
+ end
203
+ end