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.
- checksums.yaml +5 -5
- data/.github/pull_request_template.md +0 -4
- data/.github/release-drafter.yml +17 -0
- data/.github/workflows/push.yml +12 -0
- data/.rubocop.yml +1 -0
- data/.travis.yml +10 -7
- data/CHANGELOG.md +1 -589
- data/DEVELOPMENT.md +1 -1
- data/Dangerfile +1 -1
- data/Gemfile +33 -1
- data/LICENSE.txt +1 -1
- data/README.md +7 -3
- data/RELEASING.md +3 -3
- data/Rakefile +5 -0
- data/capistrano.gemspec +8 -3
- data/features/deploy.feature +16 -0
- data/features/step_definitions/assertions.rb +2 -2
- data/features/step_definitions/setup.rb +6 -4
- data/lib/capistrano/configuration/host_filter.rb +1 -2
- data/lib/capistrano/configuration/question.rb +18 -3
- data/lib/capistrano/configuration/role_filter.rb +1 -2
- data/lib/capistrano/configuration.rb +7 -3
- data/lib/capistrano/doctor/servers_doctor.rb +1 -1
- data/lib/capistrano/doctor/variables_doctor.rb +10 -3
- data/lib/capistrano/dsl/paths.rb +2 -2
- data/lib/capistrano/dsl.rb +1 -1
- data/lib/capistrano/i18n.rb +4 -0
- data/lib/capistrano/immutable_task.rb +3 -2
- data/lib/capistrano/scm/git.rb +6 -4
- data/lib/capistrano/scm/tasks/git.rake +7 -7
- data/lib/capistrano/tasks/deploy.rake +16 -6
- data/lib/capistrano/templates/deploy.rb.erb +6 -3
- data/lib/capistrano/templates/stage.rb.erb +1 -1
- data/lib/capistrano/version.rb +1 -1
- data/spec/integration/dsl_spec.rb +5 -3
- data/spec/lib/capistrano/application_spec.rb +16 -40
- data/spec/lib/capistrano/configuration/host_filter_spec.rb +10 -5
- data/spec/lib/capistrano/configuration/plugin_installer_spec.rb +1 -1
- data/spec/lib/capistrano/configuration/question_spec.rb +39 -10
- data/spec/lib/capistrano/configuration/scm_resolver_spec.rb +3 -2
- data/spec/lib/capistrano/doctor/environment_doctor_spec.rb +1 -1
- data/spec/lib/capistrano/doctor/gems_doctor_spec.rb +1 -1
- data/spec/lib/capistrano/doctor/servers_doctor_spec.rb +1 -1
- data/spec/lib/capistrano/doctor/variables_doctor_spec.rb +1 -1
- data/spec/lib/capistrano/dsl/paths_spec.rb +30 -0
- data/spec/lib/capistrano/dsl/task_enhancements_spec.rb +6 -6
- data/spec/lib/capistrano/dsl_spec.rb +5 -5
- data/spec/lib/capistrano/immutable_task_spec.rb +1 -1
- data/spec/lib/capistrano/plugin_spec.rb +2 -2
- data/spec/lib/capistrano/scm/git_spec.rb +16 -5
- data/spec/lib/capistrano/version_validator_spec.rb +23 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/support/test_app.rb +8 -3
- 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.
|
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-
|
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
|
-
|
15
|
-
|
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
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 = "
|
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"
|
data/features/deploy.feature
CHANGED
@@ -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.
|
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(
|
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
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
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.
|
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
|
-
|
42
|
+
stdin.gets
|
40
43
|
else
|
41
|
-
|
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
|
@@ -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
|
-
|
51
|
-
|
52
|
-
|
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)
|
@@ -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
|
9
|
-
#
|
10
|
-
WHITELIST = %i(
|
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
|
data/lib/capistrano/dsl/paths.rb
CHANGED
@@ -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
|
data/lib/capistrano/dsl.rb
CHANGED
data/lib/capistrano/i18n.rb
CHANGED
@@ -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
|
-
|
21
|
-
Check that you haven't loaded a Capistrano plugin in deploy.rb
|
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
|
|
data/lib/capistrano/scm/git.rb
CHANGED
@@ -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
|
-
#
|
11
|
-
#
|
12
|
-
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
-
|
161
|
-
|
162
|
-
end
|
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)}\"
|
242
|
+
execute :echo, "\"#{fetch(:current_revision)}\" > REVISION"
|
233
243
|
end
|
234
244
|
end
|
235
245
|
end
|
@@ -1,5 +1,5 @@
|
|
1
|
-
# config valid
|
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"
|
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
|
data/lib/capistrano/version.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
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
|
-
|
27
|
-
|
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
|
-
|
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
|
-
|
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("
|
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,
|
36
|
-
it_behaves_like "it filters hosts correctly", %w{server1
|
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
|
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
|
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")
|