capistrano 3.9.0 → 3.15.0

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 (54) hide show
  1. checksums.yaml +5 -5
  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 -589
  8. data/DEVELOPMENT.md +1 -1
  9. data/Dangerfile +1 -1
  10. data/Gemfile +33 -1
  11. data/LICENSE.txt +1 -1
  12. data/README.md +7 -3
  13. data/RELEASING.md +3 -3
  14. data/Rakefile +5 -0
  15. data/capistrano.gemspec +8 -3
  16. data/features/deploy.feature +16 -0
  17. data/features/step_definitions/assertions.rb +2 -2
  18. data/features/step_definitions/setup.rb +6 -4
  19. data/lib/capistrano/configuration/host_filter.rb +1 -2
  20. data/lib/capistrano/configuration/question.rb +18 -3
  21. data/lib/capistrano/configuration/role_filter.rb +1 -2
  22. data/lib/capistrano/configuration.rb +7 -3
  23. data/lib/capistrano/doctor/servers_doctor.rb +1 -1
  24. data/lib/capistrano/doctor/variables_doctor.rb +10 -3
  25. data/lib/capistrano/dsl/paths.rb +2 -2
  26. data/lib/capistrano/dsl.rb +1 -1
  27. data/lib/capistrano/i18n.rb +4 -0
  28. data/lib/capistrano/immutable_task.rb +3 -2
  29. data/lib/capistrano/scm/git.rb +6 -4
  30. data/lib/capistrano/scm/tasks/git.rake +7 -7
  31. data/lib/capistrano/tasks/deploy.rake +16 -6
  32. data/lib/capistrano/templates/deploy.rb.erb +6 -3
  33. data/lib/capistrano/templates/stage.rb.erb +1 -1
  34. data/lib/capistrano/version.rb +1 -1
  35. data/spec/integration/dsl_spec.rb +5 -3
  36. data/spec/lib/capistrano/application_spec.rb +16 -40
  37. data/spec/lib/capistrano/configuration/host_filter_spec.rb +10 -5
  38. data/spec/lib/capistrano/configuration/plugin_installer_spec.rb +1 -1
  39. data/spec/lib/capistrano/configuration/question_spec.rb +39 -10
  40. data/spec/lib/capistrano/configuration/scm_resolver_spec.rb +3 -2
  41. data/spec/lib/capistrano/doctor/environment_doctor_spec.rb +1 -1
  42. data/spec/lib/capistrano/doctor/gems_doctor_spec.rb +1 -1
  43. data/spec/lib/capistrano/doctor/servers_doctor_spec.rb +1 -1
  44. data/spec/lib/capistrano/doctor/variables_doctor_spec.rb +1 -1
  45. data/spec/lib/capistrano/dsl/paths_spec.rb +30 -0
  46. data/spec/lib/capistrano/dsl/task_enhancements_spec.rb +6 -6
  47. data/spec/lib/capistrano/dsl_spec.rb +5 -5
  48. data/spec/lib/capistrano/immutable_task_spec.rb +1 -1
  49. data/spec/lib/capistrano/plugin_spec.rb +2 -2
  50. data/spec/lib/capistrano/scm/git_spec.rb +16 -5
  51. data/spec/lib/capistrano/version_validator_spec.rb +23 -0
  52. data/spec/spec_helper.rb +13 -0
  53. data/spec/support/test_app.rb +8 -3
  54. metadata +15 -23
data/README.md CHANGED
@@ -88,6 +88,10 @@ Likewise, your server(s) will likely need supporting software installed before y
88
88
 
89
89
  Capistrano is designed to deploy using a single, non-privileged SSH user, using a *non-interactive* SSH session. If your deployment requires `sudo`, interactive prompts, authenticating as one user but running commands as another, you can probably accomplish this with Capistrano, but it may be difficult. Your automated deployments will be much smoother if you can avoid such requirements.
90
90
 
91
+ #### Shells
92
+
93
+ Capistrano 3 expects a POSIX shell like Bash or Sh. Shells like tcsh, csh, and such may work, but probably will not.
94
+
91
95
  ## Quick start
92
96
 
93
97
  ### Requirements
@@ -99,11 +103,11 @@ Capistrano is designed to deploy using a single, non-privileged SSH user, using
99
103
 
100
104
  ### Install the Capistrano gem
101
105
 
102
- Add Capistrano to your project's Gemfile:
106
+ Add Capistrano to your project's Gemfile using `require: false`:
103
107
 
104
108
  ``` ruby
105
109
  group :development do
106
- gem "capistrano", "~> 3.9"
110
+ gem "capistrano", "~> 3.14", require: false
107
111
  end
108
112
  ```
109
113
 
@@ -196,7 +200,7 @@ Contributions to Capistrano, in the form of code, documentation or idea, are gla
196
200
 
197
201
  MIT License (MIT)
198
202
 
199
- Copyright (c) 2012-2015 Tom Clements, Lee Hambley
203
+ Copyright (c) 2012-2020 Tom Clements, Lee Hambley
200
204
 
201
205
  Permission is hereby granted, free of charge, to any person obtaining a copy
202
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"
@@ -60,6 +60,22 @@ Feature: Deploy
60
60
  Then 3 valid releases are kept
61
61
  And the invalid "new" release is ignored
62
62
 
63
+ Scenario: Cleanup after many failed releases doesn't remove last good release
64
+ Given config stage file has line "set :keep_releases, 2"
65
+ And I make 2 deployments
66
+ And an invalid release named "77777777777777"
67
+ And an invalid release named "88888888888888"
68
+ And an invalid release named "99999999999999"
69
+ When I run cap "deploy:cleanup"
70
+ Then 3 valid releases are kept
71
+ And the current directory will be a symlink to the release
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
+
63
79
  Scenario: Rolling Back
64
80
  Given I make 2 deployments
65
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
@@ -66,7 +66,7 @@ Then(/^directory symlinks are created in the new release$/) do
66
66
  end
67
67
 
68
68
  Then(/^the current directory will be a symlink to the release$/) do
69
- run_vagrant_command(test_symlink_exists(TestApp.current_path))
69
+ run_vagrant_command(exists?("e", TestApp.current_path))
70
70
  end
71
71
 
72
72
  Then(/^the deploy\.rb file is created$/) do
@@ -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
 
@@ -3,8 +3,7 @@ module Capistrano
3
3
  class HostFilter
4
4
  def initialize(values)
5
5
  av = Array(values).dup
6
- av.map! { |v| v.is_a?(String) && v =~ /^(?<name>[-A-Za-z0-9.]+)(,\g<name>)*$/ ? v.split(",") : v }
7
- av.flatten!
6
+ av = av.flat_map { |v| v.is_a?(String) && v =~ /^(?<name>[-A-Za-z0-9.]+)(,\g<name>)*$/ ? v.split(",") : v }
8
7
  @rex = regex_matcher(av)
9
8
  end
10
9
 
@@ -18,6 +18,7 @@ module Capistrano
18
18
 
19
19
  def ask_question
20
20
  $stdout.print question
21
+ $stdout.flush
21
22
  end
22
23
 
23
24
  def value_or_default
@@ -35,10 +36,12 @@ module Capistrano
35
36
  end
36
37
 
37
38
  def gets
39
+ return unless stdin.tty?
40
+
38
41
  if echo?
39
- $stdin.gets
42
+ stdin.gets
40
43
  else
41
- $stdin.noecho(&:gets).tap { $stdout.print "\n" }
44
+ stdin.noecho(&:gets).tap { $stdout.print "\n" }
42
45
  end
43
46
  rescue Errno::EIO
44
47
  # when stdio gets closed
@@ -46,7 +49,11 @@ module Capistrano
46
49
  end
47
50
 
48
51
  def question
49
- 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?
50
57
  I18n.t(:question, key: key, scope: :capistrano)
51
58
  else
52
59
  I18n.t(:question_default, key: key, default_value: default, scope: :capistrano)
@@ -56,6 +63,14 @@ module Capistrano
56
63
  def echo?
57
64
  (options || {}).fetch(:echo, true)
58
65
  end
66
+
67
+ def stdin
68
+ (options || {}).fetch(:stdin, $stdin)
69
+ end
70
+
71
+ def prompt
72
+ (options || {}).fetch(:prompt, nil)
73
+ end
59
74
  end
60
75
  end
61
76
  end
@@ -3,8 +3,7 @@ module Capistrano
3
3
  class RoleFilter
4
4
  def initialize(values)
5
5
  av = Array(values).dup
6
- av.map! { |v| v.is_a?(String) ? v.split(",") : v }
7
- av.flatten!
6
+ av = av.flat_map { |v| v.is_a?(String) ? v.split(",") : v }
8
7
  @rex = regex_matcher(av)
9
8
  end
10
9
 
@@ -47,10 +47,14 @@ module Capistrano
47
47
  def any?(key)
48
48
  value = fetch(key)
49
49
  if value && value.respond_to?(:any?)
50
- value.any?
51
- else
52
- !fetch(key).nil?
50
+ begin
51
+ return value.any?
52
+ rescue ArgumentError # rubocop:disable Lint/HandleExceptions
53
+ # Gracefully ignore values whose `any?` method doesn't accept 0 args
54
+ end
53
55
  end
56
+
57
+ !value.nil?
54
58
  end
55
59
 
56
60
  def is_question?(key)
@@ -56,7 +56,7 @@ module Capistrano
56
56
  private
57
57
 
58
58
  def find_whitespace_roles
59
- servers.map(&:roles).map(&:to_a).flatten.uniq
59
+ servers.map(&:roles).flat_map(&:to_a).uniq
60
60
  .select { |role| include_whitespace?(role) }
61
61
  end
62
62
  end
@@ -5,9 +5,16 @@ module Capistrano
5
5
  # Prints a table of all Capistrano variables and their current values. If
6
6
  # there are unrecognized variables, print warnings for them.
7
7
  class VariablesDoctor
8
- # These are keys that have no default values in Capistrano, but are
9
- # nonetheless expected to be set.
10
- WHITELIST = %i(application repo_url repo_tree).freeze
8
+ # These are keys that are recognized by Capistrano, but do not have values
9
+ # set by default.
10
+ WHITELIST = %i(
11
+ application
12
+ current_directory
13
+ releases_directory
14
+ repo_url
15
+ repo_tree
16
+ shared_directory
17
+ ).freeze
11
18
  private_constant :WHITELIST
12
19
 
13
20
  include Capistrano::Doctor::OutputHelpers
@@ -15,7 +15,7 @@ module Capistrano
15
15
  end
16
16
 
17
17
  def releases_path
18
- deploy_path.join("releases")
18
+ deploy_path.join(fetch(:releases_directory, "releases"))
19
19
  end
20
20
 
21
21
  def release_path
@@ -44,7 +44,7 @@ module Capistrano
44
44
  end
45
45
 
46
46
  def shared_path
47
- deploy_path.join("shared")
47
+ deploy_path.join(fetch(:shared_directory, "shared"))
48
48
  end
49
49
 
50
50
  def revision_log
@@ -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,8 +12,12 @@ 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)",
19
+ wont_delete_current_release: "Current release was marked for being removed but it's going to be skipped on %{host}",
20
+ no_current_release: "There is no current release present on %{host}",
17
21
  no_old_releases: "No old releases (keeping newest %{keep_releases}) on %{host}",
18
22
  linked_file_does_not_exist: "linked file %{file} does not exist on %{host}",
19
23
  cannot_rollback: "There are no older releases to rollback to",
@@ -17,8 +17,9 @@ module Capistrano
17
17
 
18
18
  def enhance(*args, &block)
19
19
  $stderr.puts <<-MESSAGE
20
- WARNING: #{name} has already been invoked and can no longer be modified.
21
- Check that you haven't loaded a Capistrano plugin in deploy.rb by mistake.
20
+ ERROR: #{name} has already been invoked and can no longer be modified.
21
+ Check that you haven't loaded a Capistrano plugin in deploy.rb or a stage
22
+ (e.g. deploy/production.rb) by mistake.
22
23
  Plugins must be loaded in the Capfile to initialize properly.
23
24
  MESSAGE
24
25
 
@@ -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
@@ -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,7 +38,7 @@ 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
@@ -49,7 +49,7 @@ namespace :git do
49
49
 
50
50
  desc "Copy repo to releases"
51
51
  task create_release: :'git:update' do
52
- on release_roles :all do
52
+ on release_roles(:all), in: :groups, limit: fetch(:git_max_concurrent_connections), wait: fetch(:git_wait_interval) do
53
53
  with fetch(:git_environmental_variables) do
54
54
  within repo_path do
55
55
  execute :mkdir, "-p", release_path
@@ -61,7 +61,7 @@ namespace :git do
61
61
 
62
62
  desc "Determine the revision that will be deployed"
63
63
  task :set_current_revision do
64
- on release_roles :all do
64
+ on release_roles(:all), in: :groups, limit: fetch(:git_max_concurrent_connections), wait: fetch(:git_wait_interval) do
65
65
  within repo_path do
66
66
  with fetch(:git_environmental_variables) do
67
67
  set :current_revision, git_plugin.fetch_revision
@@ -155,12 +155,22 @@ namespace :deploy do
155
155
 
156
156
  if valid.count >= fetch(:keep_releases)
157
157
  info t(:keeping_releases, host: host.to_s, keep_releases: fetch(:keep_releases), releases: valid.count)
158
- directories = (valid - valid.last(fetch(:keep_releases)))
158
+ directories = (valid - valid.last(fetch(:keep_releases))).map do |release|
159
+ releases_path.join(release).to_s
160
+ end
161
+ if test("[ -d #{current_path} ]")
162
+ current_release = capture(:readlink, current_path).to_s
163
+ if directories.include?(current_release)
164
+ warn t(:wont_delete_current_release, host: host.to_s)
165
+ directories.delete(current_release)
166
+ end
167
+ else
168
+ debug t(:no_current_release, host: host.to_s)
169
+ end
159
170
  if directories.any?
160
- directories_str = directories.map do |release|
161
- releases_path.join(release)
162
- end.join(" ")
163
- execute :rm, "-rf", directories_str
171
+ directories.each_slice(100) do |directories_batch|
172
+ execute :rm, "-rf", *directories_batch
173
+ end
164
174
  else
165
175
  info t(:no_old_releases, host: host.to_s, keep_releases: fetch(:keep_releases))
166
176
  end
@@ -229,7 +239,7 @@ namespace :deploy do
229
239
  task :set_current_revision do
230
240
  on release_roles(:all) do
231
241
  within release_path do
232
- execute :echo, "\"#{fetch(:current_revision)}\" >> REVISION"
242
+ execute :echo, "\"#{fetch(:current_revision)}\" > REVISION"
233
243
  end
234
244
  end
235
245
  end
@@ -1,5 +1,5 @@
1
- # config valid only for current version of Capistrano
2
- lock "<%= Capistrano::VERSION %>"
1
+ # config valid for current version and patch releases of Capistrano
2
+ lock "~> <%= Capistrano::VERSION %>"
3
3
 
4
4
  set :application, "my_app_name"
5
5
  set :repo_url, "git@example.com:me/my_repo.git"
@@ -21,7 +21,7 @@ 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", "config/secrets.yml"
24
+ # append :linked_files, "config/database.yml"
25
25
 
26
26
  # Default value for linked_dirs is []
27
27
  # append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "public/system"
@@ -34,3 +34,6 @@ set :repo_url, "git@example.com:me/my_repo.git"
34
34
 
35
35
  # Default value for keep_releases is 5
36
36
  # set :keep_releases, 5
37
+
38
+ # Uncomment the following to require manually verifying the host key before first deploy.
39
+ # set :ssh_options, verify_host_key: :secure
@@ -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.9.0".freeze
2
+ VERSION = "3.15.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
@@ -10,7 +10,7 @@ module Capistrano
10
10
  Server.new("server2"),
11
11
  Server.new("server3"),
12
12
  Server.new("server4"),
13
- Server.new("server5")]
13
+ Server.new("server10")]
14
14
  end
15
15
 
16
16
  shared_examples "it filters hosts correctly" do |expected|
@@ -32,8 +32,8 @@ module Capistrano
32
32
  end
33
33
 
34
34
  context "with a comma separated string" do
35
- let(:values) { "server1,server3" }
36
- it_behaves_like "it filters hosts correctly", %w{server1 server3}
35
+ let(:values) { "server1,server10" }
36
+ it_behaves_like "it filters hosts correctly", %w{server1 server10}
37
37
  end
38
38
 
39
39
  context "with an array of strings" do
@@ -41,6 +41,11 @@ module Capistrano
41
41
  it_behaves_like "it filters hosts correctly", %w{server1 server3}
42
42
  end
43
43
 
44
+ context "with mixed splittable and unsplittable strings" do
45
+ let(:values) { %w{server1 server2,server3} }
46
+ it_behaves_like "it filters hosts correctly", %w{server1 server2 server3}
47
+ end
48
+
44
49
  context "with a regexp" do
45
50
  let(:values) { "server[13]$" }
46
51
  it_behaves_like "it filters hosts correctly", %w{server1 server3}
@@ -48,12 +53,12 @@ module Capistrano
48
53
 
49
54
  context "with a regexp with line boundaries" do
50
55
  let(:values) { "^server" }
51
- it_behaves_like "it filters hosts correctly", %w{server1 server2 server3 server4 server5}
56
+ it_behaves_like "it filters hosts correctly", %w{server1 server2 server3 server4 server10}
52
57
  end
53
58
 
54
59
  context "with a regexp with a comma" do
55
60
  let(:values) { 'server\d{1,3}$' }
56
- it_behaves_like "it filters hosts correctly", %w{server1 server2 server3 server4 server5}
61
+ it_behaves_like "it filters hosts correctly", %w{server1 server2 server3 server4 server10}
57
62
  end
58
63
 
59
64
  context "without number" do
@@ -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")