capistrano 3.4.0 → 3.17.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.
- checksums.yaml +5 -5
- data/.circleci/config.yml +129 -0
- data/.github/issue_template.md +19 -0
- data/.github/pull_request_template.md +22 -0
- data/.github/release-drafter.yml +17 -0
- data/.github/workflows/push.yml +12 -0
- data/.gitignore +8 -5
- data/.rubocop.yml +62 -0
- data/CHANGELOG.md +1 -307
- data/CONTRIBUTING.md +63 -93
- data/DEVELOPMENT.md +127 -0
- data/Dangerfile +1 -0
- data/Gemfile +40 -3
- data/LICENSE.txt +1 -1
- data/README.md +127 -44
- data/RELEASING.md +17 -0
- data/Rakefile +13 -2
- data/UPGRADING-3.7.md +86 -0
- data/bin/cap +1 -1
- data/capistrano.gemspec +21 -24
- data/features/deploy.feature +35 -1
- data/features/doctor.feature +11 -0
- data/features/installation.feature +8 -3
- data/features/stage_failure.feature +9 -0
- data/features/step_definitions/assertions.rb +51 -18
- data/features/step_definitions/cap_commands.rb +9 -0
- data/features/step_definitions/setup.rb +53 -9
- data/features/subdirectory.feature +9 -0
- data/features/support/env.rb +5 -5
- data/features/support/remote_command_helpers.rb +12 -6
- data/features/support/vagrant_helpers.rb +17 -11
- data/lib/Capfile +1 -1
- data/lib/capistrano/all.rb +10 -10
- data/lib/capistrano/application.rb +47 -34
- data/lib/capistrano/configuration/empty_filter.rb +9 -0
- data/lib/capistrano/configuration/filter.rb +17 -47
- data/lib/capistrano/configuration/host_filter.rb +29 -0
- data/lib/capistrano/configuration/null_filter.rb +9 -0
- data/lib/capistrano/configuration/plugin_installer.rb +51 -0
- data/lib/capistrano/configuration/question.rb +31 -9
- data/lib/capistrano/configuration/role_filter.rb +29 -0
- data/lib/capistrano/configuration/scm_resolver.rb +149 -0
- data/lib/capistrano/configuration/server.rb +29 -23
- data/lib/capistrano/configuration/servers.rb +21 -14
- data/lib/capistrano/configuration/validated_variables.rb +110 -0
- data/lib/capistrano/configuration/variables.rb +112 -0
- data/lib/capistrano/configuration.rb +91 -44
- data/lib/capistrano/defaults.rb +26 -4
- data/lib/capistrano/deploy.rb +1 -1
- data/lib/capistrano/doctor/environment_doctor.rb +19 -0
- data/lib/capistrano/doctor/gems_doctor.rb +45 -0
- data/lib/capistrano/doctor/output_helpers.rb +79 -0
- data/lib/capistrano/doctor/servers_doctor.rb +105 -0
- data/lib/capistrano/doctor/variables_doctor.rb +74 -0
- data/lib/capistrano/doctor.rb +6 -0
- data/lib/capistrano/dotfile.rb +1 -2
- data/lib/capistrano/dsl/env.rb +9 -47
- data/lib/capistrano/dsl/paths.rb +11 -25
- data/lib/capistrano/dsl/stages.rb +14 -2
- data/lib/capistrano/dsl/task_enhancements.rb +7 -12
- data/lib/capistrano/dsl.rb +47 -16
- data/lib/capistrano/framework.rb +1 -1
- data/lib/capistrano/i18n.rb +32 -24
- data/lib/capistrano/immutable_task.rb +30 -0
- data/lib/capistrano/install.rb +1 -1
- data/lib/capistrano/plugin.rb +95 -0
- data/lib/capistrano/proc_helpers.rb +13 -0
- data/lib/capistrano/scm/git.rb +100 -0
- data/lib/capistrano/scm/hg.rb +55 -0
- data/lib/capistrano/scm/plugin.rb +13 -0
- data/lib/capistrano/scm/svn.rb +56 -0
- data/lib/capistrano/scm/tasks/git.rake +73 -0
- data/lib/capistrano/scm/tasks/hg.rake +53 -0
- data/lib/capistrano/scm/tasks/svn.rake +53 -0
- data/lib/capistrano/scm.rb +7 -20
- data/lib/capistrano/setup.rb +20 -6
- data/lib/capistrano/tasks/console.rake +4 -8
- data/lib/capistrano/tasks/deploy.rake +105 -73
- data/lib/capistrano/tasks/doctor.rake +24 -0
- data/lib/capistrano/tasks/framework.rake +13 -14
- data/lib/capistrano/tasks/install.rake +14 -15
- data/lib/capistrano/templates/Capfile +21 -10
- data/lib/capistrano/templates/deploy.rb.erb +17 -26
- data/lib/capistrano/templates/stage.rb.erb +9 -9
- data/lib/capistrano/upload_task.rb +1 -1
- data/lib/capistrano/version.rb +1 -1
- data/lib/capistrano/version_validator.rb +5 -10
- data/spec/integration/dsl_spec.rb +289 -240
- data/spec/integration_spec_helper.rb +3 -5
- data/spec/lib/capistrano/application_spec.rb +23 -39
- data/spec/lib/capistrano/configuration/empty_filter_spec.rb +17 -0
- data/spec/lib/capistrano/configuration/filter_spec.rb +83 -85
- data/spec/lib/capistrano/configuration/host_filter_spec.rb +71 -0
- data/spec/lib/capistrano/configuration/null_filter_spec.rb +17 -0
- data/spec/lib/capistrano/configuration/plugin_installer_spec.rb +98 -0
- data/spec/lib/capistrano/configuration/question_spec.rb +58 -26
- data/spec/lib/capistrano/configuration/role_filter_spec.rb +80 -0
- data/spec/lib/capistrano/configuration/scm_resolver_spec.rb +55 -0
- data/spec/lib/capistrano/configuration/server_spec.rb +106 -113
- data/spec/lib/capistrano/configuration/servers_spec.rb +129 -145
- data/spec/lib/capistrano/configuration_spec.rb +224 -63
- data/spec/lib/capistrano/doctor/environment_doctor_spec.rb +44 -0
- data/spec/lib/capistrano/doctor/gems_doctor_spec.rb +67 -0
- data/spec/lib/capistrano/doctor/output_helpers_spec.rb +47 -0
- data/spec/lib/capistrano/doctor/servers_doctor_spec.rb +86 -0
- data/spec/lib/capistrano/doctor/variables_doctor_spec.rb +89 -0
- data/spec/lib/capistrano/dsl/paths_spec.rb +97 -59
- data/spec/lib/capistrano/dsl/task_enhancements_spec.rb +57 -37
- data/spec/lib/capistrano/dsl_spec.rb +84 -11
- data/spec/lib/capistrano/immutable_task_spec.rb +31 -0
- data/spec/lib/capistrano/plugin_spec.rb +84 -0
- data/spec/lib/capistrano/scm/git_spec.rb +184 -0
- data/spec/lib/capistrano/scm/hg_spec.rb +109 -0
- data/spec/lib/capistrano/scm/svn_spec.rb +137 -0
- data/spec/lib/capistrano/scm_spec.rb +7 -8
- data/spec/lib/capistrano/upload_task_spec.rb +7 -7
- data/spec/lib/capistrano/version_validator_spec.rb +61 -46
- data/spec/lib/capistrano_spec.rb +2 -3
- data/spec/spec_helper.rb +21 -8
- data/spec/support/Vagrantfile +9 -10
- data/spec/support/tasks/database.rake +3 -3
- data/spec/support/tasks/fail.rake +4 -3
- data/spec/support/tasks/failed.rake +2 -2
- data/spec/support/tasks/plugin.rake +6 -0
- data/spec/support/tasks/root.rake +4 -4
- data/spec/support/test_app.rb +64 -39
- metadata +100 -55
- data/.travis.yml +0 -13
- data/features/remote_file_task.feature +0 -14
- data/lib/capistrano/git.rb +0 -46
- data/lib/capistrano/hg.rb +0 -43
- data/lib/capistrano/svn.rb +0 -38
- data/lib/capistrano/tasks/git.rake +0 -81
- data/lib/capistrano/tasks/hg.rake +0 -52
- data/lib/capistrano/tasks/svn.rake +0 -52
- data/spec/lib/capistrano/git_spec.rb +0 -81
- data/spec/lib/capistrano/hg_spec.rb +0 -81
- data/spec/lib/capistrano/svn_spec.rb +0 -79
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require "capistrano/doctor/output_helpers"
|
|
2
|
+
|
|
3
|
+
module Capistrano
|
|
4
|
+
module Doctor
|
|
5
|
+
class EnvironmentDoctor
|
|
6
|
+
include Capistrano::Doctor::OutputHelpers
|
|
7
|
+
|
|
8
|
+
def call
|
|
9
|
+
title("Environment")
|
|
10
|
+
puts <<-OUT.gsub(/^\s+/, "")
|
|
11
|
+
Ruby #{RUBY_DESCRIPTION}
|
|
12
|
+
Rubygems #{Gem::VERSION}
|
|
13
|
+
Bundler #{defined?(Bundler::VERSION) ? Bundler::VERSION : 'N/A'}
|
|
14
|
+
Command #{$PROGRAM_NAME} #{ARGV.join(' ')}
|
|
15
|
+
OUT
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require "capistrano/doctor/output_helpers"
|
|
2
|
+
|
|
3
|
+
module Capistrano
|
|
4
|
+
module Doctor
|
|
5
|
+
# Prints table of all Capistrano-related gems and their version numbers. If
|
|
6
|
+
# there is a newer version of a gem available, call attention to it.
|
|
7
|
+
class GemsDoctor
|
|
8
|
+
include Capistrano::Doctor::OutputHelpers
|
|
9
|
+
|
|
10
|
+
def call
|
|
11
|
+
title("Gems")
|
|
12
|
+
table(all_gem_names) do |gem, row|
|
|
13
|
+
row.yellow if update_available?(gem)
|
|
14
|
+
row << gem
|
|
15
|
+
row << installed_gem_version(gem)
|
|
16
|
+
row << "(update available)" if update_available?(gem)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def installed_gem_version(gem_name)
|
|
23
|
+
Gem.loaded_specs[gem_name].version
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def update_available?(gem_name)
|
|
27
|
+
latest = Gem.latest_version_for(gem_name)
|
|
28
|
+
return false if latest.nil?
|
|
29
|
+
latest > installed_gem_version(gem_name)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def all_gem_names
|
|
33
|
+
core_gem_names + plugin_gem_names
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def core_gem_names
|
|
37
|
+
%w(capistrano airbrussh rake sshkit net-ssh) & Gem.loaded_specs.keys
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def plugin_gem_names
|
|
41
|
+
(Gem.loaded_specs.keys - ["capistrano"]).grep(/capistrano/).sort
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
module Capistrano
|
|
2
|
+
module Doctor
|
|
3
|
+
# Helper methods for pretty-printing doctor output to stdout. All output
|
|
4
|
+
# (other than `title`) is indented by four spaces to facilitate copying and
|
|
5
|
+
# pasting this output into e.g. GitHub or Stack Overflow to achieve code
|
|
6
|
+
# formatting.
|
|
7
|
+
module OutputHelpers
|
|
8
|
+
class Row
|
|
9
|
+
attr_reader :color
|
|
10
|
+
attr_reader :values
|
|
11
|
+
|
|
12
|
+
def initialize
|
|
13
|
+
@values = []
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def <<(value)
|
|
17
|
+
values << value
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def yellow
|
|
21
|
+
@color = :yellow
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Prints a table for a given array of records. For each record, the block
|
|
26
|
+
# is yielded two arguments: the record and a Row object. To print values
|
|
27
|
+
# for that record, add values using `row << "some value"`. A row can
|
|
28
|
+
# optionally be highlighted in yellow using `row.yellow`.
|
|
29
|
+
def table(records, &block)
|
|
30
|
+
return if records.empty?
|
|
31
|
+
rows = collect_rows(records, &block)
|
|
32
|
+
col_widths = calculate_column_widths(rows)
|
|
33
|
+
|
|
34
|
+
rows.each do |row|
|
|
35
|
+
line = row.values.each_with_index.map do |value, col|
|
|
36
|
+
value.to_s.ljust(col_widths[col])
|
|
37
|
+
end.join(" ").rstrip
|
|
38
|
+
line = color.colorize(line, row.color) if row.color
|
|
39
|
+
puts line
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Prints a title in blue with surrounding newlines.
|
|
44
|
+
def title(text)
|
|
45
|
+
# Use $stdout directly to bypass the indentation that our `puts` does.
|
|
46
|
+
$stdout.puts(color.colorize("\n#{text}\n", :blue))
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Prints text in yellow.
|
|
50
|
+
def warning(text)
|
|
51
|
+
puts color.colorize(text, :yellow)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Override `Kernel#puts` to prepend four spaces to each line.
|
|
55
|
+
def puts(string=nil)
|
|
56
|
+
$stdout.puts(string.to_s.gsub(/^/, " "))
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def collect_rows(records)
|
|
62
|
+
records.map do |rec|
|
|
63
|
+
Row.new.tap { |row| yield(rec, row) }
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def calculate_column_widths(rows)
|
|
68
|
+
num_columns = rows.map { |row| row.values.length }.max
|
|
69
|
+
Array.new(num_columns) do |col|
|
|
70
|
+
rows.map { |row| row.values[col].to_s.length }.max
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def color
|
|
75
|
+
@color ||= SSHKit::Color.new($stdout)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
require "capistrano/doctor/output_helpers"
|
|
2
|
+
|
|
3
|
+
module Capistrano
|
|
4
|
+
module Doctor
|
|
5
|
+
class ServersDoctor
|
|
6
|
+
include Capistrano::Doctor::OutputHelpers
|
|
7
|
+
|
|
8
|
+
def initialize(env=Capistrano::Configuration.env)
|
|
9
|
+
@servers = env.servers.to_a
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def call
|
|
13
|
+
title("Servers (#{servers.size})")
|
|
14
|
+
rwc = RoleWhitespaceChecker.new(servers)
|
|
15
|
+
|
|
16
|
+
table(servers) do |server, row|
|
|
17
|
+
sd = ServerDecorator.new(server)
|
|
18
|
+
|
|
19
|
+
row << sd.uri_form
|
|
20
|
+
row << sd.roles
|
|
21
|
+
row << sd.properties
|
|
22
|
+
row.yellow if rwc.any_has_whitespace?(server.roles)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
if rwc.whitespace_roles.any?
|
|
26
|
+
warning "\nWhitespace detected in role(s) #{rwc.whitespace_roles_decorated}. " \
|
|
27
|
+
"This might be a result of a mistyped \"%w()\" array literal."
|
|
28
|
+
end
|
|
29
|
+
puts
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
attr_reader :servers
|
|
35
|
+
|
|
36
|
+
class RoleWhitespaceChecker
|
|
37
|
+
attr_reader :whitespace_roles, :servers
|
|
38
|
+
|
|
39
|
+
def initialize(servers)
|
|
40
|
+
@servers = servers
|
|
41
|
+
@whitespace_roles = find_whitespace_roles
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def any_has_whitespace?(roles)
|
|
45
|
+
roles.any? { |role| include_whitespace?(role) }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def include_whitespace?(role)
|
|
49
|
+
role =~ /\s/
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def whitespace_roles_decorated
|
|
53
|
+
whitespace_roles.map(&:inspect).join(", ")
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def find_whitespace_roles
|
|
59
|
+
servers.map(&:roles).flat_map(&:to_a).uniq
|
|
60
|
+
.select { |role| include_whitespace?(role) }
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
class ServerDecorator
|
|
65
|
+
def initialize(server)
|
|
66
|
+
@server = server
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def uri_form
|
|
70
|
+
[
|
|
71
|
+
server.user,
|
|
72
|
+
server.user && "@",
|
|
73
|
+
server.hostname,
|
|
74
|
+
server.port && ":",
|
|
75
|
+
server.port
|
|
76
|
+
].compact.join
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def roles
|
|
80
|
+
server.roles.to_a.inspect
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def properties
|
|
84
|
+
return "" unless server.properties.keys.any?
|
|
85
|
+
pretty_inspect(server.properties.to_h)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
private
|
|
89
|
+
|
|
90
|
+
attr_reader :server
|
|
91
|
+
|
|
92
|
+
# Hashes with proper padding
|
|
93
|
+
def pretty_inspect(element)
|
|
94
|
+
return element.inspect unless element.is_a?(Hash)
|
|
95
|
+
|
|
96
|
+
pairs_string = element.keys.map do |key|
|
|
97
|
+
[pretty_inspect(key), pretty_inspect(element.fetch(key))].join(" => ")
|
|
98
|
+
end.join(", ")
|
|
99
|
+
|
|
100
|
+
"{ #{pairs_string} }"
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
require "capistrano/doctor/output_helpers"
|
|
2
|
+
|
|
3
|
+
module Capistrano
|
|
4
|
+
module Doctor
|
|
5
|
+
# Prints a table of all Capistrano variables and their current values. If
|
|
6
|
+
# there are unrecognized variables, print warnings for them.
|
|
7
|
+
class VariablesDoctor
|
|
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
|
+
linked_dirs
|
|
14
|
+
linked_files
|
|
15
|
+
releases_directory
|
|
16
|
+
repo_url
|
|
17
|
+
repo_tree
|
|
18
|
+
shared_directory
|
|
19
|
+
).freeze
|
|
20
|
+
private_constant :WHITELIST
|
|
21
|
+
|
|
22
|
+
include Capistrano::Doctor::OutputHelpers
|
|
23
|
+
|
|
24
|
+
def initialize(env=Capistrano::Configuration.env)
|
|
25
|
+
@env = env
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def call
|
|
29
|
+
title("Variables")
|
|
30
|
+
values = inspect_all_values
|
|
31
|
+
|
|
32
|
+
table(variables.keys.sort_by(&:to_s)) do |key, row|
|
|
33
|
+
row.yellow if suspicious_keys.include?(key)
|
|
34
|
+
row << key.inspect
|
|
35
|
+
row << values[key]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
puts if suspicious_keys.any?
|
|
39
|
+
|
|
40
|
+
suspicious_keys.sort_by(&:to_s).each do |key|
|
|
41
|
+
warning("#{key.inspect} is not a recognized Capistrano setting "\
|
|
42
|
+
"(#{location(key)})")
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
attr_reader :env
|
|
49
|
+
|
|
50
|
+
def variables
|
|
51
|
+
env.variables
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def inspect_all_values
|
|
55
|
+
variables.keys.each_with_object({}) do |key, inspected|
|
|
56
|
+
inspected[key] = if env.is_question?(key)
|
|
57
|
+
"<ask>"
|
|
58
|
+
else
|
|
59
|
+
variables.peek(key).inspect
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def suspicious_keys
|
|
65
|
+
(variables.untrusted_keys & variables.unused_keys) - WHITELIST
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def location(key)
|
|
69
|
+
loc = variables.source_locations(key).first
|
|
70
|
+
loc && loc.sub(/^#{Regexp.quote(Dir.pwd)}/, "").sub(/:in.*/, "")
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
data/lib/capistrano/dotfile.rb
CHANGED
data/lib/capistrano/dsl/env.rb
CHANGED
|
@@ -1,47 +1,14 @@
|
|
|
1
|
+
require "forwardable"
|
|
2
|
+
|
|
1
3
|
module Capistrano
|
|
2
4
|
module DSL
|
|
3
5
|
module Env
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
env.fetch(key, default, &block)
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def any?(key)
|
|
14
|
-
value = fetch(key)
|
|
15
|
-
if value && value.respond_to?(:any?)
|
|
16
|
-
value.any?
|
|
17
|
-
else
|
|
18
|
-
!fetch(key).nil?
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def set(key, value)
|
|
23
|
-
env.set(key, value)
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def set_if_empty(key, value)
|
|
27
|
-
env.set_if_empty(key, value)
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def delete(key)
|
|
31
|
-
env.delete(key)
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def ask(key, value, options={})
|
|
35
|
-
env.ask(key, value, options)
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def role(name, servers, options={})
|
|
39
|
-
env.role(name, servers, options)
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def server(name, properties={})
|
|
43
|
-
env.server(name, properties)
|
|
44
|
-
end
|
|
6
|
+
extend Forwardable
|
|
7
|
+
def_delegators :env,
|
|
8
|
+
:configure_backend, :fetch, :set, :set_if_empty, :delete,
|
|
9
|
+
:ask, :role, :server, :primary, :validate, :append,
|
|
10
|
+
:remove, :dry_run?, :install_plugin, :any?, :is_question?,
|
|
11
|
+
:configure_scm, :scm_plugin_installed?
|
|
45
12
|
|
|
46
13
|
def roles(*names)
|
|
47
14
|
env.roles_for(names.flatten)
|
|
@@ -53,17 +20,13 @@ module Capistrano
|
|
|
53
20
|
|
|
54
21
|
def release_roles(*names)
|
|
55
22
|
if names.last.is_a? Hash
|
|
56
|
-
names.last
|
|
23
|
+
names.last[:exclude] = :no_release
|
|
57
24
|
else
|
|
58
25
|
names << { exclude: :no_release }
|
|
59
26
|
end
|
|
60
27
|
roles(*names)
|
|
61
28
|
end
|
|
62
29
|
|
|
63
|
-
def primary(role)
|
|
64
|
-
env.primary(role)
|
|
65
|
-
end
|
|
66
|
-
|
|
67
30
|
def env
|
|
68
31
|
Configuration.env
|
|
69
32
|
end
|
|
@@ -75,7 +38,6 @@ module Capistrano
|
|
|
75
38
|
def asset_timestamp
|
|
76
39
|
env.timestamp.strftime("%Y%m%d%H%M.%S")
|
|
77
40
|
end
|
|
78
|
-
|
|
79
41
|
end
|
|
80
42
|
end
|
|
81
43
|
end
|
data/lib/capistrano/dsl/paths.rb
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "pathname"
|
|
2
2
|
module Capistrano
|
|
3
3
|
module DSL
|
|
4
4
|
module Paths
|
|
5
|
-
|
|
6
5
|
def deploy_to
|
|
7
6
|
fetch(:deploy_to)
|
|
8
7
|
end
|
|
@@ -12,15 +11,15 @@ module Capistrano
|
|
|
12
11
|
end
|
|
13
12
|
|
|
14
13
|
def current_path
|
|
15
|
-
deploy_path.join(
|
|
14
|
+
deploy_path.join(fetch(:current_directory, "current"))
|
|
16
15
|
end
|
|
17
16
|
|
|
18
17
|
def releases_path
|
|
19
|
-
deploy_path.join(
|
|
18
|
+
deploy_path.join(fetch(:releases_directory, "releases"))
|
|
20
19
|
end
|
|
21
20
|
|
|
22
21
|
def release_path
|
|
23
|
-
fetch(:release_path
|
|
22
|
+
fetch(:release_path) { current_path }
|
|
24
23
|
end
|
|
25
24
|
|
|
26
25
|
def set_release_path(timestamp=now)
|
|
@@ -29,40 +28,27 @@ module Capistrano
|
|
|
29
28
|
end
|
|
30
29
|
|
|
31
30
|
def stage_config_path
|
|
32
|
-
Pathname.new fetch(:stage_config_path,
|
|
31
|
+
Pathname.new fetch(:stage_config_path, "config/deploy")
|
|
33
32
|
end
|
|
34
33
|
|
|
35
34
|
def deploy_config_path
|
|
36
|
-
Pathname.new fetch(:deploy_config_path,
|
|
35
|
+
Pathname.new fetch(:deploy_config_path, "config/deploy.rb")
|
|
37
36
|
end
|
|
38
37
|
|
|
39
38
|
def repo_url
|
|
40
|
-
|
|
41
|
-
require 'uri'
|
|
42
|
-
if fetch(:git_http_username) and fetch(:git_http_password)
|
|
43
|
-
URI.parse(fetch(:repo_url)).tap do |repo_uri|
|
|
44
|
-
repo_uri.user = fetch(:git_http_username)
|
|
45
|
-
repo_uri.password = CGI.escape(fetch(:git_http_password))
|
|
46
|
-
end.to_s
|
|
47
|
-
elsif fetch(:git_http_username)
|
|
48
|
-
URI.parse(fetch(:repo_url)).tap do |repo_uri|
|
|
49
|
-
repo_uri.user = fetch(:git_http_username)
|
|
50
|
-
end.to_s
|
|
51
|
-
else
|
|
52
|
-
fetch(:repo_url)
|
|
53
|
-
end
|
|
39
|
+
fetch(:repo_url)
|
|
54
40
|
end
|
|
55
41
|
|
|
56
42
|
def repo_path
|
|
57
|
-
Pathname.new(fetch(:repo_path, ->(){deploy_path.join(
|
|
43
|
+
Pathname.new(fetch(:repo_path, ->() { deploy_path.join("repo") }))
|
|
58
44
|
end
|
|
59
45
|
|
|
60
46
|
def shared_path
|
|
61
|
-
deploy_path.join(
|
|
47
|
+
deploy_path.join(fetch(:shared_directory, "shared"))
|
|
62
48
|
end
|
|
63
49
|
|
|
64
50
|
def revision_log
|
|
65
|
-
deploy_path.join(
|
|
51
|
+
deploy_path.join("revisions.log")
|
|
66
52
|
end
|
|
67
53
|
|
|
68
54
|
def now
|
|
@@ -96,7 +82,7 @@ module Capistrano
|
|
|
96
82
|
end
|
|
97
83
|
|
|
98
84
|
def map_dirnames(paths)
|
|
99
|
-
paths.map
|
|
85
|
+
paths.map(&:dirname).uniq
|
|
100
86
|
end
|
|
101
87
|
end
|
|
102
88
|
end
|
|
@@ -1,19 +1,31 @@
|
|
|
1
1
|
module Capistrano
|
|
2
2
|
module DSL
|
|
3
3
|
module Stages
|
|
4
|
+
RESERVED_NAMES = %w(deploy doctor install).freeze
|
|
5
|
+
private_constant :RESERVED_NAMES
|
|
4
6
|
|
|
5
7
|
def stages
|
|
6
|
-
Dir[stage_definitions].map { |f| File.basename(f,
|
|
8
|
+
names = Dir[stage_definitions].map { |f| File.basename(f, ".rb") }
|
|
9
|
+
assert_valid_stage_names(names)
|
|
10
|
+
names
|
|
7
11
|
end
|
|
8
12
|
|
|
9
13
|
def stage_definitions
|
|
10
|
-
stage_config_path.join(
|
|
14
|
+
stage_config_path.join("*.rb")
|
|
11
15
|
end
|
|
12
16
|
|
|
13
17
|
def stage_set?
|
|
14
18
|
!!fetch(:stage, false)
|
|
15
19
|
end
|
|
16
20
|
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def assert_valid_stage_names(names)
|
|
24
|
+
invalid = names.find { |n| RESERVED_NAMES.include?(n) }
|
|
25
|
+
return if invalid.nil?
|
|
26
|
+
|
|
27
|
+
raise t("error.invalid_stage_name", name: invalid, path: stage_config_path.join("#{invalid}.rb"))
|
|
28
|
+
end
|
|
17
29
|
end
|
|
18
30
|
end
|
|
19
31
|
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "capistrano/upload_task"
|
|
2
2
|
|
|
3
3
|
module Capistrano
|
|
4
4
|
module TaskEnhancements
|
|
@@ -9,17 +9,14 @@ module Capistrano
|
|
|
9
9
|
|
|
10
10
|
def after(task, post_task, *args, &block)
|
|
11
11
|
Rake::Task.define_task(post_task, *args, &block) if block_given?
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
post_task.
|
|
12
|
+
task = Rake::Task[task]
|
|
13
|
+
task.enhance do
|
|
14
|
+
post = Rake.application.lookup(post_task, task.scope)
|
|
15
|
+
raise ArgumentError, "Task #{post_task.inspect} not found" unless post
|
|
16
|
+
post.invoke
|
|
15
17
|
end
|
|
16
18
|
end
|
|
17
19
|
|
|
18
|
-
def remote_file(task)
|
|
19
|
-
target_roles = task.delete(:roles) { :all }
|
|
20
|
-
define_remote_file_task(task, target_roles)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
20
|
def define_remote_file_task(task, target_roles)
|
|
24
21
|
Capistrano::UploadTask.define_task(task) do |t|
|
|
25
22
|
prerequisite_file = t.prerequisites.first
|
|
@@ -31,7 +28,6 @@ module Capistrano
|
|
|
31
28
|
upload! File.open(prerequisite_file), file
|
|
32
29
|
end
|
|
33
30
|
end
|
|
34
|
-
|
|
35
31
|
end
|
|
36
32
|
end
|
|
37
33
|
|
|
@@ -54,13 +50,12 @@ module Capistrano
|
|
|
54
50
|
|
|
55
51
|
def exit_deploy_because_of_exception(ex)
|
|
56
52
|
warn t(:deploy_failed, ex: ex.message)
|
|
57
|
-
invoke
|
|
53
|
+
invoke "deploy:failed"
|
|
58
54
|
exit(false)
|
|
59
55
|
end
|
|
60
56
|
|
|
61
57
|
def deploying?
|
|
62
58
|
fetch(:deploying, false)
|
|
63
59
|
end
|
|
64
|
-
|
|
65
60
|
end
|
|
66
61
|
end
|
data/lib/capistrano/dsl.rb
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
3
|
-
require
|
|
4
|
-
require
|
|
5
|
-
require
|
|
6
|
-
require 'capistrano/configuration/filter'
|
|
1
|
+
require "capistrano/dsl/task_enhancements"
|
|
2
|
+
require "capistrano/dsl/paths"
|
|
3
|
+
require "capistrano/dsl/stages"
|
|
4
|
+
require "capistrano/dsl/env"
|
|
5
|
+
require "capistrano/configuration/filter"
|
|
7
6
|
|
|
8
7
|
module Capistrano
|
|
9
8
|
module DSL
|
|
@@ -12,12 +11,29 @@ module Capistrano
|
|
|
12
11
|
include Paths
|
|
13
12
|
include Stages
|
|
14
13
|
|
|
15
|
-
def invoke(
|
|
16
|
-
Rake::Task[
|
|
14
|
+
def invoke(task_name, *args)
|
|
15
|
+
task = Rake::Task[task_name]
|
|
16
|
+
# NOTE: We access instance variable since the accessor was only added recently. Once Capistrano depends on rake 11+, we can revert the following line
|
|
17
|
+
if task && task.instance_variable_get(:@already_invoked)
|
|
18
|
+
file, line, = caller.first.split(":")
|
|
19
|
+
colors = SSHKit::Color.new($stderr)
|
|
20
|
+
$stderr.puts colors.colorize("Skipping task `#{task_name}'.", :yellow)
|
|
21
|
+
$stderr.puts "Capistrano tasks may only be invoked once. Since task `#{task}' was previously invoked, invoke(\"#{task_name}\") at #{file}:#{line} will be skipped."
|
|
22
|
+
$stderr.puts "If you really meant to run this task again, use invoke!(\"#{task_name}\")"
|
|
23
|
+
$stderr.puts colors.colorize("THIS BEHAVIOR MAY CHANGE IN A FUTURE VERSION OF CAPISTRANO. Please join the conversation here if this affects you.", :red)
|
|
24
|
+
$stderr.puts colors.colorize("https://github.com/capistrano/capistrano/issues/1686", :red)
|
|
25
|
+
end
|
|
26
|
+
task.invoke(*args)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def invoke!(task_name, *args)
|
|
30
|
+
task = Rake::Task[task_name]
|
|
31
|
+
task.reenable
|
|
32
|
+
task.invoke(*args)
|
|
17
33
|
end
|
|
18
34
|
|
|
19
35
|
def t(key, options={})
|
|
20
|
-
I18n.t(key, options.merge(scope: :capistrano))
|
|
36
|
+
I18n.t(key, **options.merge(scope: :capistrano))
|
|
21
37
|
end
|
|
22
38
|
|
|
23
39
|
def scm
|
|
@@ -30,12 +46,11 @@ module Capistrano
|
|
|
30
46
|
|
|
31
47
|
def revision_log_message
|
|
32
48
|
fetch(:revision_log_message,
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
)
|
|
49
|
+
t(:revision_log_message,
|
|
50
|
+
branch: fetch(:branch),
|
|
51
|
+
user: local_user,
|
|
52
|
+
sha: fetch(:current_revision),
|
|
53
|
+
release: fetch(:release_timestamp)))
|
|
39
54
|
end
|
|
40
55
|
|
|
41
56
|
def rollback_log_message
|
|
@@ -50,15 +65,31 @@ module Capistrano
|
|
|
50
65
|
VersionValidator.new(locked_version).verify
|
|
51
66
|
end
|
|
52
67
|
|
|
68
|
+
# rubocop:disable Security/MarshalLoad
|
|
53
69
|
def on(hosts, options={}, &block)
|
|
54
70
|
subset_copy = Marshal.dump(Configuration.env.filter(hosts))
|
|
55
71
|
SSHKit::Coordinator.new(Marshal.load(subset_copy)).each(options, &block)
|
|
56
72
|
end
|
|
73
|
+
# rubocop:enable Security/MarshalLoad
|
|
57
74
|
|
|
58
75
|
def run_locally(&block)
|
|
59
76
|
SSHKit::Backend::Local.new(&block).run
|
|
60
77
|
end
|
|
61
78
|
|
|
79
|
+
# Catch common beginner mistake and give a helpful error message on stderr
|
|
80
|
+
def execute(*)
|
|
81
|
+
file, line, = caller.first.split(":")
|
|
82
|
+
colors = SSHKit::Color.new($stderr)
|
|
83
|
+
$stderr.puts colors.colorize("Warning: `execute' should be wrapped in an `on' scope in #{file}:#{line}.", :red)
|
|
84
|
+
$stderr.puts
|
|
85
|
+
$stderr.puts " task :example do"
|
|
86
|
+
$stderr.puts colors.colorize(" on roles(:app) do", :yellow)
|
|
87
|
+
$stderr.puts " execute 'whoami'"
|
|
88
|
+
$stderr.puts colors.colorize(" end", :yellow)
|
|
89
|
+
$stderr.puts " end"
|
|
90
|
+
$stderr.puts
|
|
91
|
+
raise NoMethodError, "undefined method `execute' for main:Object"
|
|
92
|
+
end
|
|
62
93
|
end
|
|
63
94
|
end
|
|
64
|
-
|
|
95
|
+
extend Capistrano::DSL
|
data/lib/capistrano/framework.rb
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
load File.expand_path("../tasks/framework.rake", __FILE__)
|
|
2
|
-
require
|
|
2
|
+
require "capistrano/install"
|