capistrano 3.11.0 → 3.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.github/pull_request_template.md +0 -4
  3. data/.github/release-drafter.yml +17 -0
  4. data/.github/workflows/push.yml +12 -0
  5. data/.rubocop.yml +1 -0
  6. data/.travis.yml +10 -7
  7. data/CHANGELOG.md +1 -651
  8. data/Dangerfile +1 -1
  9. data/Gemfile +33 -1
  10. data/LICENSE.txt +1 -1
  11. data/README.md +2 -2
  12. data/RELEASING.md +3 -3
  13. data/Rakefile +5 -0
  14. data/capistrano.gemspec +8 -3
  15. data/features/deploy.feature +6 -0
  16. data/features/step_definitions/assertions.rb +1 -1
  17. data/features/step_definitions/setup.rb +6 -4
  18. data/features/support/vagrant_helpers.rb +6 -0
  19. data/lib/capistrano/configuration/question.rb +16 -4
  20. data/lib/capistrano/dsl.rb +1 -1
  21. data/lib/capistrano/i18n.rb +2 -0
  22. data/lib/capistrano/scm/git.rb +10 -4
  23. data/lib/capistrano/scm/tasks/git.rake +8 -7
  24. data/lib/capistrano/tasks/deploy.rake +3 -2
  25. data/lib/capistrano/templates/stage.rb.erb +1 -1
  26. data/lib/capistrano/version.rb +1 -1
  27. data/spec/integration/dsl_spec.rb +5 -3
  28. data/spec/lib/capistrano/application_spec.rb +16 -40
  29. data/spec/lib/capistrano/configuration/plugin_installer_spec.rb +1 -1
  30. data/spec/lib/capistrano/configuration/question_spec.rb +31 -13
  31. data/spec/lib/capistrano/configuration/scm_resolver_spec.rb +3 -2
  32. data/spec/lib/capistrano/doctor/environment_doctor_spec.rb +1 -1
  33. data/spec/lib/capistrano/doctor/gems_doctor_spec.rb +1 -1
  34. data/spec/lib/capistrano/doctor/servers_doctor_spec.rb +1 -1
  35. data/spec/lib/capistrano/doctor/variables_doctor_spec.rb +1 -1
  36. data/spec/lib/capistrano/dsl/task_enhancements_spec.rb +6 -6
  37. data/spec/lib/capistrano/dsl_spec.rb +5 -5
  38. data/spec/lib/capistrano/plugin_spec.rb +2 -2
  39. data/spec/lib/capistrano/scm/git_spec.rb +27 -5
  40. data/spec/spec_helper.rb +13 -0
  41. data/spec/support/test_app.rb +8 -3
  42. metadata +15 -23
data/Gemfile CHANGED
@@ -4,7 +4,39 @@ source "https://rubygems.org"
4
4
  gemspec
5
5
 
6
6
  group :cucumber do
7
- gem "cucumber"
7
+ # Latest versions of cucumber don't support Ruby < 2.1
8
+ # rubocop:disable Bundler/DuplicatedGem
9
+ if Gem::Requirement.new("< 2.1").satisfied_by?(Gem::Version.new(RUBY_VERSION))
10
+ gem "cucumber", "< 3.0.1"
11
+ else
12
+ gem "cucumber"
13
+ end
14
+ # rubocop:enable Bundler/DuplicatedGem
8
15
  gem "rspec"
9
16
  gem "rspec-core", "~> 3.4.4"
10
17
  end
18
+
19
+ # Latest versions of net-ssh don't support Ruby < 2.2.6
20
+ if Gem::Requirement.new("< 2.2.6").satisfied_by?(Gem::Version.new(RUBY_VERSION))
21
+ gem "net-ssh", "< 5.0.0"
22
+ end
23
+
24
+ # Latest versions of public_suffix don't support Ruby < 2.1
25
+ if Gem::Requirement.new("< 2.1").satisfied_by?(Gem::Version.new(RUBY_VERSION))
26
+ gem "public_suffix", "< 3.0.0"
27
+ end
28
+
29
+ # Latest versions of i18n don't support Ruby < 2.4
30
+ if Gem::Requirement.new("< 2.4").satisfied_by?(Gem::Version.new(RUBY_VERSION))
31
+ gem "i18n", "< 1.3.0"
32
+ end
33
+
34
+ # Latest versions of rake don't support Ruby < 2.2
35
+ if Gem::Requirement.new("< 2.2").satisfied_by?(Gem::Version.new(RUBY_VERSION))
36
+ gem "rake", "< 13.0.0"
37
+ end
38
+
39
+ # We only run danger once on a new-ish ruby; no need to install it otherwise
40
+ if Gem::Requirement.new("> 2.4").satisfied_by?(Gem::Version.new(RUBY_VERSION))
41
+ gem "danger"
42
+ end
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License (MIT)
2
2
 
3
- Copyright (c) 2012-2018 Tom Clements, Lee Hambley
3
+ Copyright (c) 2012-2020 Tom Clements, Lee Hambley
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -107,7 +107,7 @@ Add Capistrano to your project's Gemfile using `require: false`:
107
107
 
108
108
  ``` ruby
109
109
  group :development do
110
- gem "capistrano", "~> 3.11", require: false
110
+ gem "capistrano", "~> 3.16", require: false
111
111
  end
112
112
  ```
113
113
 
@@ -200,7 +200,7 @@ Contributions to Capistrano, in the form of code, documentation or idea, are gla
200
200
 
201
201
  MIT License (MIT)
202
202
 
203
- Copyright (c) 2012-2018 Tom Clements, Lee Hambley
203
+ Copyright (c) 2012-2020 Tom Clements, Lee Hambley
204
204
 
205
205
  Permission is hereby granted, free of charge, to any person obtaining a copy
206
206
  of this software and associated documentation files (the "Software"), to deal
data/RELEASING.md CHANGED
@@ -11,7 +11,7 @@
11
11
  2. **Ensure all tests are passing by running `rake spec` and `rake features`.**
12
12
  3. Determine which would be the correct next version number according to [semver](http://semver.org/).
13
13
  4. Update the version in `./lib/capistrano/version.rb`.
14
- 4. Update the version in the `./README.md` Gemfile example (`gem "capistrano", "~> X.Y"`).
15
- 5. Update the `CHANGELOG`.
16
- 6. Commit the changelog and version in a single commit, the message should be "Preparing vX.Y.Z"
14
+ 5. Update the version in the `./README.md` Gemfile example (`gem "capistrano", "~> X.Y"`).
15
+ 6. Commit the `version.rb` and `README.md` changes in a single commit, the message should be "Release vX.Y.Z"
17
16
  7. Run `rake release`; this will tag, push to GitHub, and publish to rubygems.org.
17
+ 8. Update the draft release on the [GitHub releases page](https://github.com/capistrano/capistrano/releases) to point to the new tag and publish the release
data/Rakefile CHANGED
@@ -10,3 +10,8 @@ Cucumber::Rake::Task.new(:features)
10
10
 
11
11
  desc "Run RuboCop checks"
12
12
  RuboCop::RakeTask.new
13
+
14
+ Rake::Task["release"].enhance do
15
+ puts "Don't forget to publish the release on GitHub!"
16
+ system "open https://github.com/capistrano/capistrano/releases"
17
+ end
data/capistrano.gemspec CHANGED
@@ -11,8 +11,14 @@ Gem::Specification.new do |gem|
11
11
  gem.email = ["seenmyfate@gmail.com", "lee.hambley@gmail.com"]
12
12
  gem.description = "Capistrano is a utility and framework for executing commands in parallel on multiple remote machines, via SSH."
13
13
  gem.summary = "Capistrano - Welcome to easy deployment with Ruby over SSH"
14
- gem.homepage = "http://capistranorb.com/"
15
-
14
+ gem.homepage = "https://capistranorb.com/"
15
+ gem.metadata = {
16
+ "bug_tracker_uri" => "https://github.com/capistrano/capistrano/issues",
17
+ "changelog_uri" => "https://github.com/capistrano/capistrano/releases",
18
+ "source_code_uri" => "https://github.com/capistrano/capistrano",
19
+ "homepage_uri" => "https://capistranorb.com/",
20
+ "documentation_uri" => "https://capistranorb.com/"
21
+ }
16
22
  gem.files = `git ls-files -z`.split("\x0").reject { |f| f =~ /^docs/ }
17
23
  gem.executables = %w(cap capify)
18
24
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
@@ -26,7 +32,6 @@ Gem::Specification.new do |gem|
26
32
  gem.add_dependency "rake", ">= 10.0.0"
27
33
  gem.add_dependency "sshkit", ">= 1.9.0"
28
34
 
29
- gem.add_development_dependency "danger"
30
35
  gem.add_development_dependency "mocha"
31
36
  gem.add_development_dependency "rspec"
32
37
  gem.add_development_dependency "rubocop", "0.48.1"
@@ -70,6 +70,12 @@ Feature: Deploy
70
70
  Then 3 valid releases are kept
71
71
  And the current directory will be a symlink to the release
72
72
 
73
+ Scenario: Cleanup when there are more releases than arguments can handle
74
+ Given config stage file has line "set :keep_releases, 3"
75
+ And 5000 valid existing releases
76
+ When I run cap "deploy:cleanup"
77
+ Then 3 valid releases are kept
78
+
73
79
  Scenario: Rolling Back
74
80
  Given I make 2 deployments
75
81
  When I run cap "deploy:rollback"
@@ -5,7 +5,7 @@ Then(/^references in the remote repo are listed$/) do
5
5
  end
6
6
 
7
7
  Then(/^git wrapper permissions are 0700$/) do
8
- permissions_test = %Q([ $(stat -c "%a" #{TestApp.git_wrapper_path.shellescape}) == "700" ])
8
+ permissions_test = %Q([ $(stat -c "%a" #{TestApp.git_wrapper_path_glob}) == "700" ])
9
9
  _stdout, _stderr, status = vagrant_cli_command("ssh -c #{permissions_test.shellescape}")
10
10
 
11
11
  expect(status).to be_success
@@ -80,10 +80,12 @@ end
80
80
 
81
81
  Given(/^(\d+) valid existing releases$/) do |num|
82
82
  a_day = 86_400 # in seconds
83
- offset = -(a_day * num.to_i)
84
- num.to_i.times do
85
- run_vagrant_command("mkdir -p #{TestApp.release_path(TestApp.timestamp(offset))}")
86
- offset += a_day
83
+ (1...num).each_slice(100) do |num_batch|
84
+ dirs = num_batch.map do |i|
85
+ offset = -(a_day * i)
86
+ TestApp.release_path(TestApp.timestamp(offset))
87
+ end
88
+ run_vagrant_command("mkdir -p #{dirs.join(' ')}")
87
89
  end
88
90
  end
89
91
 
@@ -30,6 +30,12 @@ module VagrantHelpers
30
30
  return [stdout, stderr] if status.success?
31
31
  raise VagrantSSHCommandError, status
32
32
  end
33
+
34
+ def puts(message)
35
+ # Attach log messages to the current cucumber feature (`log`),
36
+ # or simply puts to the console (`super`) if we are outside of cucumber.
37
+ respond_to?(:log) ? log(message) : super(message)
38
+ end
33
39
  end
34
40
 
35
41
  World(VagrantHelpers)
@@ -36,12 +36,12 @@ module Capistrano
36
36
  end
37
37
 
38
38
  def gets
39
- return unless $stdin.tty?
39
+ return unless stdin.tty?
40
40
 
41
41
  if echo?
42
- $stdin.gets
42
+ stdin.gets
43
43
  else
44
- $stdin.noecho(&:gets).tap { $stdout.print "\n" }
44
+ stdin.noecho(&:gets).tap { $stdout.print "\n" }
45
45
  end
46
46
  rescue Errno::EIO
47
47
  # when stdio gets closed
@@ -49,7 +49,11 @@ module Capistrano
49
49
  end
50
50
 
51
51
  def question
52
- if default.nil?
52
+ if prompt && default.nil?
53
+ I18n.t(:question_prompt, key: prompt, scope: :capistrano)
54
+ elsif prompt
55
+ I18n.t(:question_prompt_default, key: prompt, default_value: default, scope: :capistrano)
56
+ elsif default.nil?
53
57
  I18n.t(:question, key: key, scope: :capistrano)
54
58
  else
55
59
  I18n.t(:question_default, key: key, default_value: default, scope: :capistrano)
@@ -59,6 +63,14 @@ module Capistrano
59
63
  def echo?
60
64
  (options || {}).fetch(:echo, true)
61
65
  end
66
+
67
+ def stdin
68
+ (options || {}).fetch(:stdin, $stdin)
69
+ end
70
+
71
+ def prompt
72
+ (options || {}).fetch(:prompt, nil)
73
+ end
62
74
  end
63
75
  end
64
76
  end
@@ -33,7 +33,7 @@ module Capistrano
33
33
  end
34
34
 
35
35
  def t(key, options={})
36
- I18n.t(key, options.merge(scope: :capistrano))
36
+ I18n.t(key, **options.merge(scope: :capistrano))
37
37
  end
38
38
 
39
39
  def scm
@@ -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,6 +18,8 @@ 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
@@ -58,6 +60,10 @@ class Capistrano::SCM::Git < Capistrano::SCM::Plugin
58
60
  end
59
61
  end
60
62
 
63
+ def verify_commit
64
+ git :"verify-commit", fetch_revision
65
+ end
66
+
61
67
  def archive_to_release_path
62
68
  if (tree = fetch(:repo_tree))
63
69
  tree = tree.slice %r#^/?(.*?)/?$#, 1
@@ -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
@@ -168,8 +168,9 @@ namespace :deploy do
168
168
  debug t(:no_current_release, host: host.to_s)
169
169
  end
170
170
  if directories.any?
171
- directories_str = directories.join(" ")
172
- execute :rm, "-rf", directories_str
171
+ directories.each_slice(100) do |directories_batch|
172
+ execute :rm, "-rf", *directories_batch
173
+ end
173
174
  else
174
175
  info t(:no_old_releases, host: host.to_s, keep_releases: fetch(:keep_releases))
175
176
  end
@@ -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.16.0".freeze
3
3
  end
@@ -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
@@ -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
@@ -24,12 +25,12 @@ module Capistrano
24
25
  expect { resolver.resolve }.to output(/will not load the git scm/i).to_stderr
25
26
  end
26
27
 
27
- it "activates the git scm" do
28
+ it "activates the git scm", capture_io: true do
28
29
  resolver.resolve
29
30
  expect(Rake::Task["git:wrapper"]).not_to be_nil
30
31
  end
31
32
 
32
- it "sets :scm to :git" do
33
+ it "sets :scm to :git", capture_io: true do
33
34
  resolver.resolve
34
35
  expect(fetch(:scm)).to eq(:git)
35
36
  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