capistrano 3.11.0 → 3.19.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.docker/Dockerfile +7 -0
  3. data/.docker/ssh_key_rsa +49 -0
  4. data/.docker/ssh_key_rsa.pub +1 -0
  5. data/.docker/ubuntu_setup.sh +23 -0
  6. data/.github/pull_request_template.md +0 -4
  7. data/.github/release-drafter.yml +25 -0
  8. data/.github/workflows/ci.yml +80 -0
  9. data/.github/workflows/release-drafter.yml +18 -0
  10. data/.rubocop.yml +4 -3
  11. data/CHANGELOG.md +1 -651
  12. data/DEVELOPMENT.md +5 -20
  13. data/Gemfile +40 -3
  14. data/LICENSE.txt +1 -1
  15. data/README.md +3 -3
  16. data/RELEASING.md +3 -3
  17. data/Rakefile +13 -5
  18. data/capistrano.gemspec +8 -7
  19. data/docker-compose.yml +8 -0
  20. data/features/deploy.feature +11 -1
  21. data/features/sshconnect.feature +1 -1
  22. data/features/step_definitions/assertions.rb +34 -24
  23. data/features/step_definitions/setup.rb +15 -16
  24. data/features/support/docker_gateway.rb +53 -0
  25. data/features/support/env.rb +0 -10
  26. data/features/support/remote_command_helpers.rb +3 -3
  27. data/features/support/remote_ssh_helpers.rb +33 -0
  28. data/lib/capistrano/configuration/question.rb +16 -4
  29. data/lib/capistrano/configuration/validated_variables.rb +1 -1
  30. data/lib/capistrano/doctor/variables_doctor.rb +2 -0
  31. data/lib/capistrano/dsl.rb +1 -1
  32. data/lib/capistrano/i18n.rb +2 -0
  33. data/lib/capistrano/scm/git.rb +15 -4
  34. data/lib/capistrano/scm/tasks/git.rake +19 -7
  35. data/lib/capistrano/tasks/deploy.rake +26 -3
  36. data/lib/capistrano/templates/deploy.rb.erb +2 -2
  37. data/lib/capistrano/templates/stage.rb.erb +1 -1
  38. data/lib/capistrano/version.rb +1 -1
  39. data/spec/integration/dsl_spec.rb +16 -14
  40. data/spec/lib/capistrano/application_spec.rb +16 -40
  41. data/spec/lib/capistrano/configuration/plugin_installer_spec.rb +1 -1
  42. data/spec/lib/capistrano/configuration/question_spec.rb +31 -13
  43. data/spec/lib/capistrano/configuration/scm_resolver_spec.rb +4 -2
  44. data/spec/lib/capistrano/doctor/environment_doctor_spec.rb +1 -1
  45. data/spec/lib/capistrano/doctor/gems_doctor_spec.rb +1 -1
  46. data/spec/lib/capistrano/doctor/servers_doctor_spec.rb +1 -1
  47. data/spec/lib/capistrano/doctor/variables_doctor_spec.rb +1 -1
  48. data/spec/lib/capistrano/dsl/task_enhancements_spec.rb +6 -6
  49. data/spec/lib/capistrano/dsl_spec.rb +5 -5
  50. data/spec/lib/capistrano/plugin_spec.rb +2 -2
  51. data/spec/lib/capistrano/scm/git_spec.rb +37 -5
  52. data/spec/spec_helper.rb +13 -0
  53. data/spec/support/test_app.rb +23 -14
  54. metadata +25 -73
  55. data/.travis.yml +0 -27
  56. data/Dangerfile +0 -1
  57. data/features/support/vagrant_helpers.rb +0 -35
  58. data/spec/support/.gitignore +0 -1
  59. data/spec/support/Vagrantfile +0 -23
@@ -12,6 +12,8 @@ en = {
12
12
  written_file: "create %{file}",
13
13
  question: "Please enter %{key}: ",
14
14
  question_default: "Please enter %{key} (%{default_value}): ",
15
+ question_prompt: "%{key}: ",
16
+ question_prompt_default: "%{key} (%{default_value}): ",
15
17
  keeping_releases: "Keeping %{keep_releases} of %{releases} deployed releases on %{host}",
16
18
  skip_cleanup: "Skipping cleanup of invalid releases on %{host}; unexpected foldername found (should be timestamp)",
17
19
  wont_delete_current_release: "Current release was marked for being removed but it's going to be skipped on %{host}",
@@ -1,5 +1,6 @@
1
1
  require "capistrano/scm/plugin"
2
2
  require "cgi"
3
+ require "securerandom"
3
4
  require "shellwords"
4
5
  require "uri"
5
6
 
@@ -7,10 +8,9 @@ class Capistrano::SCM::Git < Capistrano::SCM::Plugin
7
8
  def set_defaults
8
9
  set_if_empty :git_shallow_clone, false
9
10
  set_if_empty :git_wrapper_path, lambda {
10
- # Try to avoid permissions issues when multiple users deploy the same app
11
- # by using different file names in the same dir for each deployer and stage.
12
- suffix = %i(application stage local_user).map { |key| fetch(key).to_s }.join("-")
13
- "#{fetch(:tmp_dir)}/git-ssh-#{suffix}.sh"
11
+ # Use a unique name that won't collide with other deployments, and
12
+ # that cannot be guessed by other processes that have access to /tmp.
13
+ "#{fetch(:tmp_dir)}/git-ssh-#{SecureRandom.hex(10)}.sh"
14
14
  }
15
15
  set_if_empty :git_environmental_variables, lambda {
16
16
  {
@@ -18,12 +18,15 @@ class Capistrano::SCM::Git < Capistrano::SCM::Plugin
18
18
  git_ssh: fetch(:git_wrapper_path)
19
19
  }
20
20
  }
21
+ set_if_empty :git_max_concurrent_connections, 10
22
+ set_if_empty :git_wait_interval, 0
21
23
  end
22
24
 
23
25
  def register_hooks
24
26
  after "deploy:new_release_path", "git:create_release"
25
27
  before "deploy:check", "git:check"
26
28
  before "deploy:set_current_revision", "git:set_current_revision"
29
+ before "deploy:set_current_revision_time", "git:set_current_revision_time"
27
30
  end
28
31
 
29
32
  def define_tasks
@@ -58,6 +61,10 @@ class Capistrano::SCM::Git < Capistrano::SCM::Plugin
58
61
  end
59
62
  end
60
63
 
64
+ def verify_commit
65
+ git :"verify-commit", fetch_revision
66
+ end
67
+
61
68
  def archive_to_release_path
62
69
  if (tree = fetch(:repo_tree))
63
70
  tree = tree.slice %r#^/?(.*?)/?$#, 1
@@ -72,6 +79,10 @@ class Capistrano::SCM::Git < Capistrano::SCM::Plugin
72
79
  backend.capture(:git, "rev-list --max-count=1 #{fetch(:branch)}")
73
80
  end
74
81
 
82
+ def fetch_revision_time
83
+ backend.capture(:git, "--no-pager log -1 --pretty=format:\"%ct\" #{fetch(:branch)}")
84
+ end
85
+
75
86
  def git(*args)
76
87
  args.unshift :git
77
88
  backend.execute(*args)
@@ -4,9 +4,9 @@ git_plugin = self
4
4
  namespace :git do
5
5
  desc "Upload the git wrapper script, this script guarantees that we can script git without getting an interactive prompt"
6
6
  task :wrapper do
7
- on release_roles :all do
7
+ on release_roles(:all), in: :groups, limit: fetch(:git_max_concurrent_connections), wait: fetch(:git_wait_interval) do
8
8
  execute :mkdir, "-p", File.dirname(fetch(:git_wrapper_path)).shellescape
9
- upload! StringIO.new("#!/bin/sh -e\nexec /usr/bin/ssh -o PasswordAuthentication=no -o StrictHostKeyChecking=no \"$@\"\n"), fetch(:git_wrapper_path)
9
+ upload! StringIO.new("#!/bin/sh -e\nexec /usr/bin/env ssh -o PasswordAuthentication=no -o StrictHostKeyChecking=no \"$@\"\n"), fetch(:git_wrapper_path)
10
10
  execute :chmod, "700", fetch(:git_wrapper_path).shellescape
11
11
  end
12
12
  end
@@ -14,7 +14,7 @@ namespace :git do
14
14
  desc "Check that the repository is reachable"
15
15
  task check: :'git:wrapper' do
16
16
  fetch(:branch)
17
- on release_roles :all do
17
+ on release_roles(:all), in: :groups, limit: fetch(:git_max_concurrent_connections), wait: fetch(:git_wait_interval) do
18
18
  with fetch(:git_environmental_variables) do
19
19
  git_plugin.check_repo_is_reachable
20
20
  end
@@ -23,7 +23,7 @@ namespace :git do
23
23
 
24
24
  desc "Clone the repo to the cache"
25
25
  task clone: :'git:wrapper' do
26
- on release_roles :all do
26
+ on release_roles(:all), in: :groups, limit: fetch(:git_max_concurrent_connections), wait: fetch(:git_wait_interval) do
27
27
  if git_plugin.repo_mirror_exists?
28
28
  info t(:mirror_exists, at: repo_path)
29
29
  else
@@ -38,10 +38,11 @@ namespace :git do
38
38
 
39
39
  desc "Update the repo mirror to reflect the origin state"
40
40
  task update: :'git:clone' do
41
- on release_roles :all do
41
+ on release_roles(:all), in: :groups, limit: fetch(:git_max_concurrent_connections), wait: fetch(:git_wait_interval) do
42
42
  within repo_path do
43
43
  with fetch(:git_environmental_variables) do
44
44
  git_plugin.update_mirror
45
+ git_plugin.verify_commit if fetch(:git_verify_commit)
45
46
  end
46
47
  end
47
48
  end
@@ -49,7 +50,7 @@ namespace :git do
49
50
 
50
51
  desc "Copy repo to releases"
51
52
  task create_release: :'git:update' do
52
- on release_roles :all do
53
+ on release_roles(:all), in: :groups, limit: fetch(:git_max_concurrent_connections), wait: fetch(:git_wait_interval) do
53
54
  with fetch(:git_environmental_variables) do
54
55
  within repo_path do
55
56
  execute :mkdir, "-p", release_path
@@ -61,7 +62,7 @@ namespace :git do
61
62
 
62
63
  desc "Determine the revision that will be deployed"
63
64
  task :set_current_revision do
64
- on release_roles :all do
65
+ on release_roles(:all), in: :groups, limit: fetch(:git_max_concurrent_connections), wait: fetch(:git_wait_interval) do
65
66
  within repo_path do
66
67
  with fetch(:git_environmental_variables) do
67
68
  set :current_revision, git_plugin.fetch_revision
@@ -69,4 +70,15 @@ namespace :git do
69
70
  end
70
71
  end
71
72
  end
73
+
74
+ desc "Determine the unix timestamp that the revision that will be deployed was created"
75
+ task :set_current_revision_time do
76
+ on release_roles(:all), in: :groups, limit: fetch(:git_max_concurrent_connections), wait: fetch(:git_wait_interval) do
77
+ within repo_path do
78
+ with fetch(:git_environmental_variables) do
79
+ set :current_revision_time, git_plugin.fetch_revision_time
80
+ end
81
+ end
82
+ end
83
+ end
72
84
  end
@@ -3,6 +3,7 @@ namespace :deploy do
3
3
  invoke "deploy:print_config_variables" if fetch(:print_config_variables, false)
4
4
  invoke "deploy:check"
5
5
  invoke "deploy:set_previous_revision"
6
+ invoke "deploy:set_previous_revision_time"
6
7
  end
7
8
 
8
9
  task :print_config_variables do
@@ -27,6 +28,7 @@ namespace :deploy do
27
28
 
28
29
  task updating: :new_release_path do
29
30
  invoke "deploy:set_current_revision"
31
+ invoke "deploy:set_current_revision_time"
30
32
  invoke "deploy:symlink:shared"
31
33
  end
32
34
 
@@ -168,8 +170,9 @@ namespace :deploy do
168
170
  debug t(:no_current_release, host: host.to_s)
169
171
  end
170
172
  if directories.any?
171
- directories_str = directories.join(" ")
172
- execute :rm, "-rf", directories_str
173
+ directories.each_slice(100) do |directories_batch|
174
+ execute :rm, "-rf", *directories_batch
175
+ end
173
176
  else
174
177
  info t(:no_old_releases, host: host.to_s, keep_releases: fetch(:keep_releases))
175
178
  end
@@ -235,7 +238,7 @@ namespace :deploy do
235
238
  end
236
239
 
237
240
  desc "Place a REVISION file with the current revision SHA in the current release path"
238
- task :set_current_revision do
241
+ task :set_current_revision do
239
242
  on release_roles(:all) do
240
243
  within release_path do
241
244
  execute :echo, "\"#{fetch(:current_revision)}\" > REVISION"
@@ -252,6 +255,26 @@ namespace :deploy do
252
255
  end
253
256
  end
254
257
 
258
+ desc "Place a REVISION_TIME file with the current revision commit time in the current release path"
259
+ task :set_current_revision_time do
260
+ on release_roles(:all) do
261
+ within release_path do
262
+ if fetch(:current_revision_time)
263
+ execute :echo, "\"#{fetch(:current_revision_time)}\" > REVISION_TIME"
264
+ end
265
+ end
266
+ end
267
+ end
268
+
269
+ task :set_previous_revision_time do
270
+ on release_roles(:all) do
271
+ target = release_path.join("REVISION_TIME")
272
+ if test "[ -f #{target} ]"
273
+ set(:previous_revision_time, capture(:cat, target, "2>/dev/null"))
274
+ end
275
+ end
276
+ end
277
+
255
278
  task :restart
256
279
  task :failed
257
280
  end
@@ -21,10 +21,10 @@ set :repo_url, "git@example.com:me/my_repo.git"
21
21
  # set :pty, true
22
22
 
23
23
  # Default value for :linked_files is []
24
- # append :linked_files, "config/database.yml"
24
+ # append :linked_files, "config/database.yml", 'config/master.key'
25
25
 
26
26
  # Default value for linked_dirs is []
27
- # append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "public/system"
27
+ # append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "public/system", "vendor", "storage"
28
28
 
29
29
  # Default value for default_env is {}
30
30
  # set :default_env, { path: "/opt/ruby/bin:$PATH" }
@@ -42,7 +42,7 @@
42
42
  # Global options
43
43
  # --------------
44
44
  # set :ssh_options, {
45
- # keys: %w(/home/rlisowski/.ssh/id_rsa),
45
+ # keys: %w(/home/user_name/.ssh/id_rsa),
46
46
  # forward_agent: false,
47
47
  # auth_methods: %w(password)
48
48
  # }
@@ -1,3 +1,3 @@
1
1
  module Capistrano
2
- VERSION = "3.11.0".freeze
2
+ VERSION = "3.19.1".freeze
3
3
  end
@@ -247,7 +247,7 @@ describe Capistrano::DSL do
247
247
  end
248
248
  end
249
249
 
250
- context "when the attribute `primary` is explicity set" do
250
+ context "when the attribute `primary` is explicitly set" do
251
251
  subject { dsl.primary(:app) }
252
252
  it "returns the servers" do
253
253
  expect(subject.hostname).to eq "example4.com"
@@ -356,14 +356,16 @@ describe Capistrano::DSL do
356
356
  end
357
357
 
358
358
  describe "asking for a variable" do
359
+ let(:stdin) { stub(tty?: true) }
360
+
359
361
  before do
360
- dsl.ask(:scm, :svn)
362
+ dsl.ask(:scm, :svn, stdin: stdin)
361
363
  $stdout.stubs(:print)
362
364
  end
363
365
 
364
366
  context "variable is provided" do
365
367
  before do
366
- $stdin.expects(:gets).returns("git")
368
+ stdin.expects(:gets).returns("git")
367
369
  end
368
370
 
369
371
  it "sets the input as the variable" do
@@ -373,7 +375,7 @@ describe Capistrano::DSL do
373
375
 
374
376
  context "variable is not provided" do
375
377
  before do
376
- $stdin.expects(:gets).returns("")
378
+ stdin.expects(:gets).returns("")
377
379
  end
378
380
 
379
381
  it "sets the variable as the default" do
@@ -590,8 +592,8 @@ describe Capistrano::DSL do
590
592
 
591
593
  it "yields the properties for a single role" do
592
594
  recipient = mock("recipient")
593
- recipient.expects(:doit).with("example1.com", :redis, port: 6379, type: :slave)
594
- recipient.expects(:doit).with("example2.com", :redis, port: 6379, type: :master)
595
+ recipient.expects(:doit).with("example1.com", :redis, { port: 6379, type: :slave })
596
+ recipient.expects(:doit).with("example2.com", :redis, { port: 6379, type: :master })
595
597
  dsl.role_properties(:redis) do |host, role, props|
596
598
  recipient.doit(host, role, props)
597
599
  end
@@ -599,8 +601,8 @@ describe Capistrano::DSL do
599
601
 
600
602
  it "yields the properties for multiple roles" do
601
603
  recipient = mock("recipient")
602
- recipient.expects(:doit).with("example1.com", :redis, port: 6379, type: :slave)
603
- recipient.expects(:doit).with("example2.com", :redis, port: 6379, type: :master)
604
+ recipient.expects(:doit).with("example1.com", :redis, { port: 6379, type: :slave })
605
+ recipient.expects(:doit).with("example2.com", :redis, { port: 6379, type: :master })
604
606
  recipient.expects(:doit).with("example3.com", :app, nil)
605
607
  dsl.role_properties(:redis, :app) do |host, role, props|
606
608
  recipient.doit(host, role, props)
@@ -609,10 +611,10 @@ describe Capistrano::DSL do
609
611
 
610
612
  it "yields the merged properties for multiple roles" do
611
613
  recipient = mock("recipient")
612
- recipient.expects(:doit).with("example1.com", :redis, port: 6379, type: :slave)
613
- recipient.expects(:doit).with("example2.com", :redis, port: 6379, type: :master)
614
- recipient.expects(:doit).with("example1.com", :web, port: 80)
615
- recipient.expects(:doit).with("example2.com", :web, port: 81)
614
+ recipient.expects(:doit).with("example1.com", :redis, { port: 6379, type: :slave })
615
+ recipient.expects(:doit).with("example2.com", :redis, { port: 6379, type: :master })
616
+ recipient.expects(:doit).with("example1.com", :web, { port: 80 })
617
+ recipient.expects(:doit).with("example2.com", :web, { port: 81 })
616
618
  dsl.role_properties(:redis, :web) do |host, role, props|
617
619
  recipient.doit(host, role, props)
618
620
  end
@@ -620,8 +622,8 @@ describe Capistrano::DSL do
620
622
 
621
623
  it "honours a property filter before yielding" do
622
624
  recipient = mock("recipient")
623
- recipient.expects(:doit).with("example1.com", :redis, port: 6379, type: :slave)
624
- recipient.expects(:doit).with("example1.com", :web, port: 80)
625
+ recipient.expects(:doit).with("example1.com", :redis, { port: 6379, type: :slave })
626
+ recipient.expects(:doit).with("example1.com", :web, { port: 80 })
625
627
  dsl.role_properties(:redis, :web, select: :active) do |host, role, props|
626
628
  recipient.doit(host, role, props)
627
629
  end
@@ -5,46 +5,40 @@ describe Capistrano::Application do
5
5
 
6
6
  it "provides a --format option which enables the choice of output formatting"
7
7
 
8
- let(:help_output) do
9
- out, _err = capture_io do
10
- flags "--help", "-h"
11
- end
12
- out
13
- end
14
-
15
- it "displays documentation URL as help banner" do
16
- expect(help_output.lines.first).to match(/capistranorb.com/)
8
+ it "displays documentation URL as help banner", capture_io: true do
9
+ flags "--help", "-h"
10
+ expect($stdout.string.each_line.first).to match(/capistranorb.com/)
17
11
  end
18
12
 
19
13
  %w(quiet silent verbose).each do |switch|
20
- it "doesn't include --#{switch} in help" do
21
- expect(help_output).not_to match(/--#{switch}/)
14
+ it "doesn't include --#{switch} in help", capture_io: true do
15
+ flags "--help", "-h"
16
+ expect($stdout.string).not_to match(/--#{switch}/)
22
17
  end
23
18
  end
24
19
 
25
- it "overrides the rake method, but still prints the rake version" do
26
- out, _err = capture_io do
27
- flags "--version", "-V"
28
- end
20
+ it "overrides the rake method, but still prints the rake version", capture_io: true do
21
+ flags "--version", "-V"
22
+ out = $stdout.string
29
23
  expect(out).to match(/\bCapistrano Version\b/)
30
24
  expect(out).to match(/\b#{Capistrano::VERSION}\b/)
31
25
  expect(out).to match(/\bRake Version\b/)
32
26
  expect(out).to match(/\b#{Rake::VERSION}\b/)
33
27
  end
34
28
 
35
- it "overrides the rake method, and sets the sshkit_backend to SSHKit::Backend::Printer" do
36
- capture_io do
37
- flags "--dry-run", "-n"
38
- end
29
+ it "overrides the rake method, and sets the sshkit_backend to SSHKit::Backend::Printer", capture_io: true do
30
+ flags "--dry-run", "-n"
39
31
  sshkit_backend = Capistrano::Configuration.fetch(:sshkit_backend)
40
32
  expect(sshkit_backend).to eq(SSHKit::Backend::Printer)
41
33
  end
42
34
 
43
- it "enables printing all config variables on command line parameter" do
44
- capture_io do
35
+ it "enables printing all config variables on command line parameter", capture_io: true do
36
+ begin
45
37
  flags "--print-config-variables", "-p"
38
+ expect(Capistrano::Configuration.fetch(:print_config_variables)).to be true
39
+ ensure
40
+ Capistrano::Configuration.reset!
46
41
  end
47
- expect(Capistrano::Configuration.fetch(:print_config_variables)).to be true
48
42
  end
49
43
 
50
44
  def flags(*sets)
@@ -63,22 +57,4 @@ describe Capistrano::Application do
63
57
  subject.run
64
58
  subject.options
65
59
  end
66
-
67
- def capture_io
68
- require "stringio"
69
-
70
- orig_stdout = $stdout
71
- orig_stderr = $stderr
72
- captured_stdout = StringIO.new
73
- captured_stderr = StringIO.new
74
- $stdout = captured_stdout
75
- $stderr = captured_stderr
76
-
77
- yield
78
-
79
- return captured_stdout.string, captured_stderr.string
80
- ensure
81
- $stdout = orig_stdout
82
- $stderr = orig_stderr
83
- end
84
60
  end
@@ -49,7 +49,7 @@ module Capistrano
49
49
  expect(task.prerequisites).to eq([:example_prerequisite])
50
50
  end
51
51
 
52
- it "sets defaults when load:defaults is invoked" do
52
+ it "sets defaults when load:defaults is invoked", capture_io: true do
53
53
  expect(fetch(:example_variable)).to be_nil
54
54
  invoke "load:defaults"
55
55
  expect(fetch(:example_variable)).to eq("foo")
@@ -3,12 +3,14 @@ require "spec_helper"
3
3
  module Capistrano
4
4
  class Configuration
5
5
  describe Question do
6
- let(:question) { Question.new(key, default, options) }
7
- let(:question_without_echo) { Question.new(key, default, echo: false) }
8
- let(:question_without_default) { Question.new(key, nil) }
6
+ let(:question) { Question.new(key, default, stdin: stdin) }
7
+ let(:question_without_echo) { Question.new(key, default, echo: false, stdin: stdin) }
8
+ let(:question_without_default) { Question.new(key, nil, stdin: stdin) }
9
+ let(:question_prompt) { Question.new(key, default, stdin: stdin, prompt: "Your favorite branch") }
10
+ let(:question_prompt_without_default) { Question.new(key, nil, stdin: stdin, prompt: "Your favorite branch") }
9
11
  let(:default) { :default }
10
12
  let(:key) { :branch }
11
- let(:options) { nil }
13
+ let(:stdin) { stub(tty?: true) }
12
14
 
13
15
  describe ".new" do
14
16
  it "takes a key, default, options" do
@@ -22,15 +24,15 @@ module Capistrano
22
24
 
23
25
  it "returns the echoed value" do
24
26
  $stdout.expects(:print).with("Please enter branch (default): ")
25
- $stdin.expects(:gets).returns(branch)
26
- $stdin.expects(:noecho).never
27
+ stdin.expects(:gets).returns(branch)
28
+ stdin.expects(:noecho).never
27
29
 
28
30
  expect(question.call).to eq(branch)
29
31
  end
30
32
 
31
33
  it "returns the value but does not echo it" do
32
34
  $stdout.expects(:print).with("Please enter branch (default): ")
33
- $stdin.expects(:noecho).returns(branch)
35
+ stdin.expects(:noecho).returns(branch)
34
36
  $stdout.expects(:print).with("\n")
35
37
 
36
38
  expect(question_without_echo.call).to eq(branch)
@@ -38,11 +40,27 @@ module Capistrano
38
40
 
39
41
  it "returns the value but has no default between parenthesis" do
40
42
  $stdout.expects(:print).with("Please enter branch: ")
41
- $stdin.expects(:gets).returns(branch)
42
- $stdin.expects(:noecho).never
43
+ stdin.expects(:gets).returns(branch)
44
+ stdin.expects(:noecho).never
43
45
 
44
46
  expect(question_without_default.call).to eq(branch)
45
47
  end
48
+
49
+ it "uses prompt and returns the value" do
50
+ $stdout.expects(:print).with("Your favorite branch (default): ")
51
+ stdin.expects(:gets).returns(branch)
52
+ stdin.expects(:noecho).never
53
+
54
+ expect(question_prompt.call).to eq(branch)
55
+ end
56
+
57
+ it "uses prompt and returns the value but has no default between parenthesis" do
58
+ $stdout.expects(:print).with("Your favorite branch: ")
59
+ stdin.expects(:gets).returns(branch)
60
+ stdin.expects(:noecho).never
61
+
62
+ expect(question_prompt_without_default.call).to eq(branch)
63
+ end
46
64
  end
47
65
 
48
66
  context "value is not entered" do
@@ -50,7 +68,7 @@ module Capistrano
50
68
 
51
69
  before do
52
70
  $stdout.expects(:print).with("Please enter branch (default): ")
53
- $stdin.expects(:gets).returns("")
71
+ stdin.expects(:gets).returns("")
54
72
  end
55
73
 
56
74
  it "returns the default as the value" do
@@ -58,10 +76,10 @@ module Capistrano
58
76
  end
59
77
  end
60
78
 
61
- context "tty unavailable" do
79
+ context "tty unavailable", capture_io: true do
62
80
  before do
63
- $stdin.expects(:gets).never
64
- $stdin.expects(:tty?).returns(false)
81
+ stdin.expects(:gets).never
82
+ stdin.expects(:tty?).returns(false)
65
83
  end
66
84
 
67
85
  it "returns the default as the value" do
@@ -1,4 +1,5 @@
1
1
  require "spec_helper"
2
+ require "capistrano/scm"
2
3
 
3
4
  module Capistrano
4
5
  class Configuration
@@ -11,6 +12,7 @@ module Capistrano
11
12
  Rake::Task.define_task("deploy:check")
12
13
  Rake::Task.define_task("deploy:new_release_path")
13
14
  Rake::Task.define_task("deploy:set_current_revision")
15
+ Rake::Task.define_task("deploy:set_current_revision_time")
14
16
  set :scm, SCMResolver::DEFAULT_GIT
15
17
  end
16
18
 
@@ -24,12 +26,12 @@ module Capistrano
24
26
  expect { resolver.resolve }.to output(/will not load the git scm/i).to_stderr
25
27
  end
26
28
 
27
- it "activates the git scm" do
29
+ it "activates the git scm", capture_io: true do
28
30
  resolver.resolve
29
31
  expect(Rake::Task["git:wrapper"]).not_to be_nil
30
32
  end
31
33
 
32
- it "sets :scm to :git" do
34
+ it "sets :scm to :git", capture_io: true do
33
35
  resolver.resolve
34
36
  expect(fetch(:scm)).to eq(:git)
35
37
  end
@@ -29,7 +29,7 @@ module Capistrano
29
29
  Rake::Task.clear
30
30
  end
31
31
 
32
- it "has an doctor:environment task that calls EnvironmentDoctor" do
32
+ it "has an doctor:environment task that calls EnvironmentDoctor", capture_io: true do
33
33
  EnvironmentDoctor.any_instance.expects(:call)
34
34
  Rake::Task["doctor:environment"].invoke
35
35
  end
@@ -53,7 +53,7 @@ module Capistrano
53
53
  Rake::Task.clear
54
54
  end
55
55
 
56
- it "has an doctor:gems task that calls GemsDoctor" do
56
+ it "has an doctor:gems task that calls GemsDoctor", capture_io: true do
57
57
  GemsDoctor.any_instance.expects(:call)
58
58
  Rake::Task["doctor:gems"].invoke
59
59
  end
@@ -71,7 +71,7 @@ module Capistrano
71
71
  Rake::Task.clear
72
72
  end
73
73
 
74
- it "has an doctor:servers task that calls ServersDoctor" do
74
+ it "has an doctor:servers task that calls ServersDoctor", capture_io: true do
75
75
  ServersDoctor.any_instance.expects(:call)
76
76
  Rake::Task["doctor:servers"].invoke
77
77
  end
@@ -74,7 +74,7 @@ module Capistrano
74
74
  Rake::Task.clear
75
75
  end
76
76
 
77
- it "has an doctor:variables task that calls VariablesDoctor" do
77
+ it "has an doctor:variables task that calls VariablesDoctor", capture_io: true do
78
78
  VariablesDoctor.any_instance.expects(:call)
79
79
  Rake::Task["doctor:variables"].invoke
80
80
  end
@@ -35,7 +35,7 @@ module Capistrano
35
35
  end
36
36
  end
37
37
 
38
- it "invokes in proper order if define after than before" do
38
+ it "invokes in proper order if define after than before", capture_io: true do
39
39
  task_enhancements.after("task", "after_task")
40
40
  task_enhancements.before("task", "before_task")
41
41
 
@@ -44,7 +44,7 @@ module Capistrano
44
44
  expect(order).to eq(%w(before_task task after_task))
45
45
  end
46
46
 
47
- it "invokes in proper order if define before than after" do
47
+ it "invokes in proper order if define before than after", capture_io: true do
48
48
  task_enhancements.before("task", "before_task")
49
49
  task_enhancements.after("task", "after_task")
50
50
 
@@ -53,7 +53,7 @@ module Capistrano
53
53
  expect(order).to eq(%w(before_task task after_task))
54
54
  end
55
55
 
56
- it "invokes in proper order when referring to as-yet undefined tasks" do
56
+ it "invokes in proper order when referring to as-yet undefined tasks", capture_io: true do
57
57
  task_enhancements.after("task", "not_loaded_task")
58
58
 
59
59
  Rake::Task.define_task("not_loaded_task") do
@@ -65,7 +65,7 @@ module Capistrano
65
65
  expect(order).to eq(%w(task not_loaded_task))
66
66
  end
67
67
 
68
- it "invokes in proper order and with arguments and block" do
68
+ it "invokes in proper order and with arguments and block", capture_io: true do
69
69
  task_enhancements.after("task", "after_task_custom", :order) do |_t, _args|
70
70
  order.push "after_task"
71
71
  end
@@ -79,7 +79,7 @@ module Capistrano
79
79
  expect(order).to eq(%w(before_task task after_task))
80
80
  end
81
81
 
82
- it "invokes using the correct namespace when defined within a namespace" do
82
+ it "invokes using the correct namespace when defined within a namespace", capture_io: true do
83
83
  Rake.application.in_namespace("namespace") do
84
84
  Rake::Task.define_task("task") do |t|
85
85
  order.push(t.name)
@@ -99,7 +99,7 @@ module Capistrano
99
99
  )
100
100
  end
101
101
 
102
- it "raises a sensible error if the task isn't found" do
102
+ it "raises a sensible error if the task isn't found", capture_io: true do
103
103
  task_enhancements.after("task", "non_existent_task")
104
104
  expect { Rake::Task["task"].invoke order }.to raise_error(ArgumentError, 'Task "non_existent_task" not found')
105
105
  end