capistrano 2.8.0 → 3.19.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 +7 -0
- data/.docker/Dockerfile +7 -0
- data/.docker/ssh_key_rsa +49 -0
- data/.docker/ssh_key_rsa.pub +1 -0
- data/.docker/ubuntu_setup.sh +23 -0
- data/.github/issue_template.md +19 -0
- data/.github/pull_request_template.md +22 -0
- data/.github/release-drafter.yml +25 -0
- data/.github/workflows/ci.yml +80 -0
- data/.github/workflows/release-drafter.yml +18 -0
- data/.gitignore +23 -8
- data/.rubocop.yml +62 -0
- data/CHANGELOG.md +1 -0
- data/CONTRIBUTING.md +63 -0
- data/DEVELOPMENT.md +112 -0
- data/Gemfile +42 -9
- data/LICENSE.txt +21 -0
- data/README.md +221 -0
- data/RELEASING.md +17 -0
- data/Rakefile +17 -8
- data/UPGRADING-3.7.md +86 -0
- data/bin/cap +2 -3
- data/bin/capify +7 -89
- data/capistrano.gemspec +29 -43
- data/docker-compose.yml +8 -0
- data/features/configuration.feature +28 -0
- data/features/deploy.feature +92 -0
- data/features/deploy_failure.feature +17 -0
- data/features/doctor.feature +11 -0
- data/features/installation.feature +21 -0
- data/features/sshconnect.feature +11 -0
- data/features/stage_failure.feature +9 -0
- data/features/step_definitions/assertions.rb +162 -0
- data/features/step_definitions/cap_commands.rb +21 -0
- data/features/step_definitions/setup.rb +91 -0
- data/features/subdirectory.feature +9 -0
- data/features/support/docker_gateway.rb +53 -0
- data/features/support/env.rb +1 -0
- data/features/support/remote_command_helpers.rb +29 -0
- data/features/support/remote_ssh_helpers.rb +33 -0
- data/lib/Capfile +3 -0
- data/lib/capistrano/all.rb +17 -0
- data/lib/capistrano/application.rb +153 -0
- data/lib/capistrano/configuration/empty_filter.rb +9 -0
- data/lib/capistrano/configuration/filter.rb +26 -0
- 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 +76 -0
- data/lib/capistrano/configuration/role_filter.rb +29 -0
- data/lib/capistrano/configuration/scm_resolver.rb +149 -0
- data/lib/capistrano/configuration/server.rb +137 -0
- data/lib/capistrano/configuration/servers.rb +56 -96
- data/lib/capistrano/configuration/validated_variables.rb +110 -0
- data/lib/capistrano/configuration/variables.rb +79 -94
- data/lib/capistrano/configuration.rb +178 -33
- data/lib/capistrano/console.rb +1 -0
- data/lib/capistrano/defaults.rb +36 -0
- data/lib/capistrano/deploy.rb +3 -0
- 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 +2 -0
- data/lib/capistrano/dsl/env.rb +43 -0
- data/lib/capistrano/dsl/paths.rb +89 -0
- data/lib/capistrano/dsl/stages.rb +31 -0
- data/lib/capistrano/dsl/task_enhancements.rb +61 -0
- data/lib/capistrano/dsl.rb +95 -0
- data/lib/capistrano/framework.rb +2 -0
- data/lib/capistrano/i18n.rb +46 -0
- data/lib/capistrano/immutable_task.rb +30 -0
- data/lib/capistrano/install.rb +1 -0
- data/lib/capistrano/plugin.rb +95 -0
- data/lib/capistrano/proc_helpers.rb +13 -0
- data/lib/capistrano/scm/git.rb +105 -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 +84 -0
- data/lib/capistrano/scm/tasks/hg.rake +53 -0
- data/lib/capistrano/scm/tasks/svn.rake +53 -0
- data/lib/capistrano/scm.rb +115 -0
- data/lib/capistrano/setup.rb +36 -0
- data/lib/capistrano/tasks/console.rake +25 -0
- data/lib/capistrano/tasks/deploy.rake +280 -0
- data/lib/capistrano/tasks/doctor.rake +24 -0
- data/lib/capistrano/tasks/framework.rake +67 -0
- data/lib/capistrano/tasks/install.rake +41 -0
- data/lib/capistrano/templates/Capfile +38 -0
- data/lib/capistrano/templates/deploy.rb.erb +39 -0
- data/lib/capistrano/templates/stage.rb.erb +61 -0
- data/lib/capistrano/upload_task.rb +9 -0
- data/lib/capistrano/version.rb +1 -14
- data/lib/capistrano/version_validator.rb +32 -0
- data/lib/capistrano.rb +0 -3
- data/spec/integration/dsl_spec.rb +632 -0
- data/spec/integration_spec_helper.rb +5 -0
- data/spec/lib/capistrano/application_spec.rb +60 -0
- data/spec/lib/capistrano/configuration/empty_filter_spec.rb +17 -0
- data/spec/lib/capistrano/configuration/filter_spec.rb +109 -0
- 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 +92 -0
- data/spec/lib/capistrano/configuration/role_filter_spec.rb +80 -0
- data/spec/lib/capistrano/configuration/scm_resolver_spec.rb +56 -0
- data/spec/lib/capistrano/configuration/server_spec.rb +309 -0
- data/spec/lib/capistrano/configuration/servers_spec.rb +331 -0
- data/spec/lib/capistrano/configuration_spec.rb +357 -0
- 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 +228 -0
- data/spec/lib/capistrano/dsl/task_enhancements_spec.rb +108 -0
- data/spec/lib/capistrano/dsl_spec.rb +125 -0
- 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 +194 -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 +103 -0
- data/spec/lib/capistrano/upload_task_spec.rb +19 -0
- data/spec/lib/capistrano/version_validator_spec.rb +118 -0
- data/spec/lib/capistrano_spec.rb +7 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/support/matchers.rb +5 -0
- data/spec/support/tasks/database.rake +11 -0
- data/spec/support/tasks/fail.rake +8 -0
- data/spec/support/tasks/failed.rake +5 -0
- data/spec/support/tasks/plugin.rake +6 -0
- data/spec/support/tasks/root.rake +11 -0
- data/spec/support/test_app.rb +205 -0
- metadata +234 -208
- data/.rvmrc +0 -1
- data/CHANGELOG +0 -954
- data/README.mdown +0 -76
- data/lib/capistrano/callback.rb +0 -45
- data/lib/capistrano/cli/execute.rb +0 -85
- data/lib/capistrano/cli/help.rb +0 -125
- data/lib/capistrano/cli/help.txt +0 -81
- data/lib/capistrano/cli/options.rb +0 -243
- data/lib/capistrano/cli/ui.rb +0 -40
- data/lib/capistrano/cli.rb +0 -47
- data/lib/capistrano/command.rb +0 -286
- data/lib/capistrano/configuration/actions/file_transfer.rb +0 -51
- data/lib/capistrano/configuration/actions/inspect.rb +0 -46
- data/lib/capistrano/configuration/actions/invocation.rb +0 -298
- data/lib/capistrano/configuration/callbacks.rb +0 -148
- data/lib/capistrano/configuration/connections.rb +0 -230
- data/lib/capistrano/configuration/execution.rb +0 -143
- data/lib/capistrano/configuration/loading.rb +0 -197
- data/lib/capistrano/configuration/namespaces.rb +0 -197
- data/lib/capistrano/configuration/roles.rb +0 -73
- data/lib/capistrano/errors.rb +0 -19
- data/lib/capistrano/ext/string.rb +0 -5
- data/lib/capistrano/extensions.rb +0 -57
- data/lib/capistrano/logger.rb +0 -59
- data/lib/capistrano/processable.rb +0 -53
- data/lib/capistrano/recipes/compat.rb +0 -32
- data/lib/capistrano/recipes/deploy/assets.rb +0 -57
- data/lib/capistrano/recipes/deploy/dependencies.rb +0 -44
- data/lib/capistrano/recipes/deploy/local_dependency.rb +0 -54
- data/lib/capistrano/recipes/deploy/remote_dependency.rb +0 -111
- data/lib/capistrano/recipes/deploy/scm/accurev.rb +0 -169
- data/lib/capistrano/recipes/deploy/scm/base.rb +0 -196
- data/lib/capistrano/recipes/deploy/scm/bzr.rb +0 -86
- data/lib/capistrano/recipes/deploy/scm/cvs.rb +0 -153
- data/lib/capistrano/recipes/deploy/scm/darcs.rb +0 -96
- data/lib/capistrano/recipes/deploy/scm/git.rb +0 -282
- data/lib/capistrano/recipes/deploy/scm/mercurial.rb +0 -137
- data/lib/capistrano/recipes/deploy/scm/none.rb +0 -44
- data/lib/capistrano/recipes/deploy/scm/perforce.rb +0 -138
- data/lib/capistrano/recipes/deploy/scm/subversion.rb +0 -121
- data/lib/capistrano/recipes/deploy/scm.rb +0 -19
- data/lib/capistrano/recipes/deploy/strategy/base.rb +0 -88
- data/lib/capistrano/recipes/deploy/strategy/checkout.rb +0 -20
- data/lib/capistrano/recipes/deploy/strategy/copy.rb +0 -224
- data/lib/capistrano/recipes/deploy/strategy/export.rb +0 -20
- data/lib/capistrano/recipes/deploy/strategy/remote.rb +0 -52
- data/lib/capistrano/recipes/deploy/strategy/remote_cache.rb +0 -57
- data/lib/capistrano/recipes/deploy/strategy.rb +0 -19
- data/lib/capistrano/recipes/deploy/templates/maintenance.rhtml +0 -53
- data/lib/capistrano/recipes/deploy.rb +0 -568
- data/lib/capistrano/recipes/standard.rb +0 -37
- data/lib/capistrano/recipes/templates/maintenance.rhtml +0 -53
- data/lib/capistrano/role.rb +0 -102
- data/lib/capistrano/server_definition.rb +0 -56
- data/lib/capistrano/shell.rb +0 -260
- data/lib/capistrano/ssh.rb +0 -101
- data/lib/capistrano/task_definition.rb +0 -75
- data/lib/capistrano/transfer.rb +0 -216
- data/rvmrc.sample +0 -1
- data/test/cli/execute_test.rb +0 -132
- data/test/cli/help_test.rb +0 -165
- data/test/cli/options_test.rb +0 -329
- data/test/cli/ui_test.rb +0 -28
- data/test/cli_test.rb +0 -17
- data/test/command_test.rb +0 -289
- data/test/configuration/actions/file_transfer_test.rb +0 -61
- data/test/configuration/actions/inspect_test.rb +0 -65
- data/test/configuration/actions/invocation_test.rb +0 -247
- data/test/configuration/callbacks_test.rb +0 -220
- data/test/configuration/connections_test.rb +0 -420
- data/test/configuration/execution_test.rb +0 -175
- data/test/configuration/loading_test.rb +0 -132
- data/test/configuration/namespace_dsl_test.rb +0 -311
- data/test/configuration/roles_test.rb +0 -144
- data/test/configuration/servers_test.rb +0 -183
- data/test/configuration/variables_test.rb +0 -190
- data/test/configuration_test.rb +0 -88
- data/test/deploy/local_dependency_test.rb +0 -76
- data/test/deploy/remote_dependency_test.rb +0 -135
- data/test/deploy/scm/accurev_test.rb +0 -23
- data/test/deploy/scm/base_test.rb +0 -55
- data/test/deploy/scm/bzr_test.rb +0 -51
- data/test/deploy/scm/darcs_test.rb +0 -37
- data/test/deploy/scm/git_test.rb +0 -184
- data/test/deploy/scm/mercurial_test.rb +0 -134
- data/test/deploy/scm/none_test.rb +0 -35
- data/test/deploy/scm/subversion_test.rb +0 -32
- data/test/deploy/strategy/copy_test.rb +0 -321
- data/test/extensions_test.rb +0 -69
- data/test/fixtures/cli_integration.rb +0 -5
- data/test/fixtures/config.rb +0 -5
- data/test/fixtures/custom.rb +0 -3
- data/test/logger_test.rb +0 -123
- data/test/recipes_test.rb +0 -25
- data/test/role_test.rb +0 -11
- data/test/server_definition_test.rb +0 -121
- data/test/shell_test.rb +0 -90
- data/test/ssh_test.rb +0 -113
- data/test/task_definition_test.rb +0 -116
- data/test/transfer_test.rb +0 -160
- data/test/utils.rb +0 -37
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require "capistrano/configuration"
|
|
2
|
+
require "capistrano/configuration/empty_filter"
|
|
3
|
+
require "capistrano/configuration/host_filter"
|
|
4
|
+
require "capistrano/configuration/null_filter"
|
|
5
|
+
require "capistrano/configuration/role_filter"
|
|
6
|
+
|
|
7
|
+
module Capistrano
|
|
8
|
+
class Configuration
|
|
9
|
+
class Filter
|
|
10
|
+
def initialize(type, values=nil)
|
|
11
|
+
raise "Invalid filter type #{type}" unless %i(host role).include? type
|
|
12
|
+
av = Array(values)
|
|
13
|
+
@strategy = if av.empty? then EmptyFilter.new
|
|
14
|
+
elsif av.include?(:all) || av.include?("all") then NullFilter.new
|
|
15
|
+
elsif type == :host then HostFilter.new(values)
|
|
16
|
+
elsif type == :role then RoleFilter.new(values)
|
|
17
|
+
else NullFilter.new
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def filter(servers)
|
|
22
|
+
@strategy.filter servers
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Capistrano
|
|
2
|
+
class Configuration
|
|
3
|
+
class HostFilter
|
|
4
|
+
def initialize(values)
|
|
5
|
+
av = Array(values).dup
|
|
6
|
+
av = av.flat_map { |v| v.is_a?(String) && v =~ /^(?<name>[-A-Za-z0-9.]+)(,\g<name>)*$/ ? v.split(",") : v }
|
|
7
|
+
@rex = regex_matcher(av)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def filter(servers)
|
|
11
|
+
Array(servers).select { |s| @rex.match s.to_s }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def regex_matcher(values)
|
|
17
|
+
values.map! do |v|
|
|
18
|
+
case v
|
|
19
|
+
when Regexp then v
|
|
20
|
+
else
|
|
21
|
+
vs = v.to_s
|
|
22
|
+
vs =~ /^[-A-Za-z0-9.]+$/ ? /^#{Regexp.quote(vs)}$/ : Regexp.new(vs)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
Regexp.union values
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Encapsulates the logic for installing plugins into Capistrano. Plugins must
|
|
2
|
+
# simply conform to a basic API; the PluginInstaller takes care of invoking the
|
|
3
|
+
# API at appropriate times.
|
|
4
|
+
#
|
|
5
|
+
# This class is not used directly; instead it is typically accessed via the
|
|
6
|
+
# `install_plugin` method of the Capistrano DSL.
|
|
7
|
+
#
|
|
8
|
+
module Capistrano
|
|
9
|
+
class Configuration
|
|
10
|
+
class PluginInstaller
|
|
11
|
+
# "Installs" a Plugin into Capistrano by loading its tasks, hooks, and
|
|
12
|
+
# defaults at the appropriate time. The hooks in particular can be
|
|
13
|
+
# skipped, if you want full control over when and how the plugin's tasks
|
|
14
|
+
# are executed. Simply pass `load_hooks:false` to opt out.
|
|
15
|
+
#
|
|
16
|
+
# The plugin class or instance may be provided. These are equivalent:
|
|
17
|
+
#
|
|
18
|
+
# install(Capistrano::SCM::Git)
|
|
19
|
+
# install(Capistrano::SCM::Git.new)
|
|
20
|
+
#
|
|
21
|
+
# Note that the :load_immediately flag is for internal use only and will
|
|
22
|
+
# be removed in an upcoming release.
|
|
23
|
+
#
|
|
24
|
+
def install(plugin, load_hooks: true, load_immediately: false)
|
|
25
|
+
plugin = plugin.is_a?(Class) ? plugin.new : plugin
|
|
26
|
+
|
|
27
|
+
plugin.define_tasks
|
|
28
|
+
plugin.register_hooks if load_hooks
|
|
29
|
+
@scm_installed ||= provides_scm?(plugin)
|
|
30
|
+
|
|
31
|
+
if load_immediately
|
|
32
|
+
plugin.set_defaults
|
|
33
|
+
else
|
|
34
|
+
Rake::Task.define_task("load:defaults") do
|
|
35
|
+
plugin.set_defaults
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def scm_installed?
|
|
41
|
+
@scm_installed
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def provides_scm?(plugin)
|
|
47
|
+
plugin.respond_to?(:scm?) && plugin.scm?
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
module Capistrano
|
|
2
|
+
class Configuration
|
|
3
|
+
class Question
|
|
4
|
+
def initialize(key, default, options={})
|
|
5
|
+
@key = key
|
|
6
|
+
@default = default
|
|
7
|
+
@options = options
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def call
|
|
11
|
+
ask_question
|
|
12
|
+
value_or_default
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
attr_reader :key, :default, :options
|
|
18
|
+
|
|
19
|
+
def ask_question
|
|
20
|
+
$stdout.print question
|
|
21
|
+
$stdout.flush
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def value_or_default
|
|
25
|
+
if response.empty?
|
|
26
|
+
default
|
|
27
|
+
else
|
|
28
|
+
response
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def response
|
|
33
|
+
return @response if defined? @response
|
|
34
|
+
|
|
35
|
+
@response = (gets || "").chomp
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def gets
|
|
39
|
+
return unless stdin.tty?
|
|
40
|
+
|
|
41
|
+
if echo?
|
|
42
|
+
stdin.gets
|
|
43
|
+
else
|
|
44
|
+
stdin.noecho(&:gets).tap { $stdout.print "\n" }
|
|
45
|
+
end
|
|
46
|
+
rescue Errno::EIO
|
|
47
|
+
# when stdio gets closed
|
|
48
|
+
return
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def question
|
|
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?
|
|
57
|
+
I18n.t(:question, key: key, scope: :capistrano)
|
|
58
|
+
else
|
|
59
|
+
I18n.t(:question_default, key: key, default_value: default, scope: :capistrano)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def echo?
|
|
64
|
+
(options || {}).fetch(:echo, true)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def stdin
|
|
68
|
+
(options || {}).fetch(:stdin, $stdin)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def prompt
|
|
72
|
+
(options || {}).fetch(:prompt, nil)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Capistrano
|
|
2
|
+
class Configuration
|
|
3
|
+
class RoleFilter
|
|
4
|
+
def initialize(values)
|
|
5
|
+
av = Array(values).dup
|
|
6
|
+
av = av.flat_map { |v| v.is_a?(String) ? v.split(",") : v }
|
|
7
|
+
@rex = regex_matcher(av)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def filter(servers)
|
|
11
|
+
Array(servers).select { |s| s.is_a?(String) ? false : s.roles.any? { |r| @rex.match r } }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def regex_matcher(values)
|
|
17
|
+
values.map! do |v|
|
|
18
|
+
case v
|
|
19
|
+
when Regexp then v
|
|
20
|
+
else
|
|
21
|
+
vs = v.to_s
|
|
22
|
+
vs =~ %r{^/(.+)/$} ? Regexp.new($1) : /^#{Regexp.quote(vs)}$/
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
Regexp.union values
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
module Capistrano
|
|
2
|
+
class Configuration
|
|
3
|
+
# In earlier versions of Capistrano, users would specify the desired SCM
|
|
4
|
+
# implementation using `set :scm, :git`, for example. Capistrano would then
|
|
5
|
+
# load the matching .rb file based on this variable.
|
|
6
|
+
#
|
|
7
|
+
# Now we expect users to explicitly `require` and call `new` on the desired
|
|
8
|
+
# SCM implementation in their Capfile. The `set` technique is deprecated.
|
|
9
|
+
#
|
|
10
|
+
# This SCMResolver class takes care of managing the transition from the old
|
|
11
|
+
# to new system. It maintains the legacy behavior, but prints deprecation
|
|
12
|
+
# warnings when it is used.
|
|
13
|
+
#
|
|
14
|
+
# To maintain backwards compatibility, the resolver will load the Git SCM by
|
|
15
|
+
# if default it determines that no SCM has been explicitly specified or
|
|
16
|
+
# loaded. To force no SCM to be used at all, use `set :scm, nil`. This hack
|
|
17
|
+
# won't be necessary once backwards compatibility is removed in a future
|
|
18
|
+
# version.
|
|
19
|
+
#
|
|
20
|
+
# TODO: Remove this class entirely in Capistrano 4.0.
|
|
21
|
+
#
|
|
22
|
+
class SCMResolver
|
|
23
|
+
DEFAULT_GIT = :"default-git"
|
|
24
|
+
|
|
25
|
+
include Capistrano::DSL
|
|
26
|
+
|
|
27
|
+
def resolve
|
|
28
|
+
return if scm_name.nil?
|
|
29
|
+
set(:scm, :git) if using_default_scm?
|
|
30
|
+
|
|
31
|
+
print_deprecation_warnings_if_applicable
|
|
32
|
+
|
|
33
|
+
# Note that `scm_plugin_installed?` comes from Capistrano::DSL
|
|
34
|
+
if scm_plugin_installed?
|
|
35
|
+
delete(:scm)
|
|
36
|
+
return
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
if built_in_scm_name?
|
|
40
|
+
load_built_in_scm
|
|
41
|
+
else
|
|
42
|
+
# Compatibility with existing 3.x third-party SCMs
|
|
43
|
+
register_legacy_scm_hooks
|
|
44
|
+
load_legacy_scm_by_name
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
def using_default_scm?
|
|
51
|
+
return @using_default_scm if defined? @using_default_scm
|
|
52
|
+
@using_default_scm = (fetch(:scm) == DEFAULT_GIT)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def scm_name
|
|
56
|
+
fetch(:scm)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def load_built_in_scm
|
|
60
|
+
require "capistrano/scm/#{scm_name}"
|
|
61
|
+
scm_class = Object.const_get(built_in_scm_plugin_class_name)
|
|
62
|
+
# We use :load_immediately because we are initializing the SCM plugin
|
|
63
|
+
# late in the load process and therefore can't use the standard
|
|
64
|
+
# load:defaults technique.
|
|
65
|
+
install_plugin(scm_class, load_immediately: true)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def load_legacy_scm_by_name
|
|
69
|
+
load("capistrano/#{scm_name}.rb")
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def third_party_scm_name?
|
|
73
|
+
!built_in_scm_name?
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def built_in_scm_name?
|
|
77
|
+
%w(git hg svn).include?(scm_name.to_s.downcase)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def built_in_scm_plugin_class_name
|
|
81
|
+
"Capistrano::SCM::#{scm_name.to_s.capitalize}"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# rubocop:disable Style/GuardClause
|
|
85
|
+
def register_legacy_scm_hooks
|
|
86
|
+
if Rake::Task.task_defined?("deploy:new_release_path")
|
|
87
|
+
after "deploy:new_release_path", "#{scm_name}:create_release"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
if Rake::Task.task_defined?("deploy:check")
|
|
91
|
+
before "deploy:check", "#{scm_name}:check"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
if Rake::Task.task_defined?("deploy:set_current_revision")
|
|
95
|
+
before "deploy:set_current_revision",
|
|
96
|
+
"#{scm_name}:set_current_revision"
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
# rubocop:enable Style/GuardClause
|
|
100
|
+
|
|
101
|
+
def print_deprecation_warnings_if_applicable
|
|
102
|
+
if using_default_scm?
|
|
103
|
+
warn_add_git_to_capfile unless scm_plugin_installed?
|
|
104
|
+
elsif built_in_scm_name?
|
|
105
|
+
warn_set_scm_is_deprecated
|
|
106
|
+
elsif third_party_scm_name?
|
|
107
|
+
warn_third_party_scm_must_be_upgraded
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def warn_set_scm_is_deprecated
|
|
112
|
+
$stderr.puts(<<-MESSAGE)
|
|
113
|
+
[Deprecation Notice] `set :scm, #{scm_name.inspect}` is deprecated.
|
|
114
|
+
To ensure your project is compatible with future versions of Capistrano,
|
|
115
|
+
remove the :scm setting and instead add these lines to your Capfile after
|
|
116
|
+
`require "capistrano/deploy"`:
|
|
117
|
+
|
|
118
|
+
require "capistrano/scm/#{scm_name}"
|
|
119
|
+
install_plugin #{built_in_scm_plugin_class_name}
|
|
120
|
+
|
|
121
|
+
MESSAGE
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def warn_add_git_to_capfile
|
|
125
|
+
$stderr.puts(<<-MESSAGE)
|
|
126
|
+
[Deprecation Notice] Future versions of Capistrano will not load the Git SCM
|
|
127
|
+
plugin by default. To silence this deprecation warning, add the following to
|
|
128
|
+
your Capfile after `require "capistrano/deploy"`:
|
|
129
|
+
|
|
130
|
+
require "capistrano/scm/git"
|
|
131
|
+
install_plugin Capistrano::SCM::Git
|
|
132
|
+
|
|
133
|
+
MESSAGE
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def warn_third_party_scm_must_be_upgraded
|
|
137
|
+
$stderr.puts(<<-MESSAGE)
|
|
138
|
+
[Deprecation Notice] `set :scm, #{scm_name.inspect}` is deprecated.
|
|
139
|
+
To ensure this custom SCM will work with future versions of Capistrano,
|
|
140
|
+
please upgrade it to a version that uses the new SCM plugin mechanism
|
|
141
|
+
documented here:
|
|
142
|
+
|
|
143
|
+
http://capistranorb.com/documentation/advanced-features/custom-scm
|
|
144
|
+
|
|
145
|
+
MESSAGE
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
require "set"
|
|
2
|
+
module Capistrano
|
|
3
|
+
class Configuration
|
|
4
|
+
class Server < SSHKit::Host
|
|
5
|
+
extend Forwardable
|
|
6
|
+
def_delegators :properties, :roles, :fetch, :set
|
|
7
|
+
|
|
8
|
+
def self.[](host)
|
|
9
|
+
host.is_a?(Server) ? host : new(host)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def add_roles(roles)
|
|
13
|
+
Array(roles).each { |role| add_role(role) }
|
|
14
|
+
self
|
|
15
|
+
end
|
|
16
|
+
alias roles= add_roles
|
|
17
|
+
|
|
18
|
+
def add_role(role)
|
|
19
|
+
roles.add role.to_sym
|
|
20
|
+
self
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def has_role?(role)
|
|
24
|
+
roles.include? role.to_sym
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def select?(options)
|
|
28
|
+
options.each do |k, v|
|
|
29
|
+
callable = v.respond_to?(:call) ? v : ->(server) { server.fetch(v) }
|
|
30
|
+
result = \
|
|
31
|
+
case k
|
|
32
|
+
when :filter, :select
|
|
33
|
+
callable.call(self)
|
|
34
|
+
when :exclude
|
|
35
|
+
!callable.call(self)
|
|
36
|
+
else
|
|
37
|
+
fetch(k) == v
|
|
38
|
+
end
|
|
39
|
+
return false unless result
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
true
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def primary
|
|
46
|
+
self if fetch(:primary)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def with(properties)
|
|
50
|
+
properties.each { |key, value| add_property(key, value) }
|
|
51
|
+
self
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def properties
|
|
55
|
+
@properties ||= Properties.new
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def netssh_options
|
|
59
|
+
@netssh_options ||= super.merge(fetch(:ssh_options) || {})
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def roles_array
|
|
63
|
+
roles.to_a
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def matches?(other)
|
|
67
|
+
# This matching logic must stay in sync with `Servers#add_host`.
|
|
68
|
+
hostname == other.hostname && port == other.port
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
def add_property(key, value)
|
|
74
|
+
if respond_to?("#{key}=")
|
|
75
|
+
send("#{key}=", value)
|
|
76
|
+
else
|
|
77
|
+
set(key, value)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
class Properties
|
|
82
|
+
def initialize
|
|
83
|
+
@properties = {}
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def set(key, value)
|
|
87
|
+
pval = @properties[key]
|
|
88
|
+
if pval.is_a?(Hash) && value.is_a?(Hash)
|
|
89
|
+
pval.merge!(value)
|
|
90
|
+
elsif pval.is_a?(Set) && value.is_a?(Set)
|
|
91
|
+
pval.merge(value)
|
|
92
|
+
elsif pval.is_a?(Array) && value.is_a?(Array)
|
|
93
|
+
pval.concat value
|
|
94
|
+
else
|
|
95
|
+
@properties[key] = value
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def fetch(key)
|
|
100
|
+
@properties[key]
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def respond_to_missing?(method, _include_all=false)
|
|
104
|
+
@properties.key?(method) || super
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def roles
|
|
108
|
+
@roles ||= Set.new
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def keys
|
|
112
|
+
@properties.keys
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# rubocop:disable Style/MethodMissing
|
|
116
|
+
def method_missing(key, value=nil)
|
|
117
|
+
if value
|
|
118
|
+
set(lvalue(key), value)
|
|
119
|
+
else
|
|
120
|
+
fetch(key)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
# rubocop:enable Style/MethodMissing
|
|
124
|
+
|
|
125
|
+
def to_h
|
|
126
|
+
@properties
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
private
|
|
130
|
+
|
|
131
|
+
def lvalue(key)
|
|
132
|
+
key.to_s.chomp("=").to_sym
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
@@ -1,115 +1,75 @@
|
|
|
1
|
+
require "set"
|
|
2
|
+
require "capistrano/configuration"
|
|
3
|
+
require "capistrano/configuration/filter"
|
|
4
|
+
|
|
1
5
|
module Capistrano
|
|
2
6
|
class Configuration
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
# The options hash accepts the same arguments as #find_servers, and any
|
|
6
|
-
# preexisting options there will take precedence over the options in
|
|
7
|
-
# the task.
|
|
8
|
-
def find_servers_for_task(task, options={})
|
|
9
|
-
find_servers(task.options.merge(options))
|
|
10
|
-
end
|
|
7
|
+
class Servers
|
|
8
|
+
include Enumerable
|
|
11
9
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
# Additionally, if the HOSTS environment variable is set, it will take
|
|
22
|
-
# precedence over any other options. Similarly, the ROLES environment
|
|
23
|
-
# variable will take precedence over other options. If both HOSTS and
|
|
24
|
-
# ROLES are given, HOSTS wins.
|
|
25
|
-
#
|
|
26
|
-
# Yet additionally, if the HOSTFILTER environment variable is set, it
|
|
27
|
-
# will limit the result to hosts found in that (comma-separated) list.
|
|
28
|
-
#
|
|
29
|
-
# If the HOSTROLEFILTER environment variable is set, it will limit the
|
|
30
|
-
# result to hosts found in that (comma-separated) list of roles
|
|
31
|
-
#
|
|
32
|
-
# Usage:
|
|
33
|
-
#
|
|
34
|
-
# # return all known servers
|
|
35
|
-
# servers = find_servers
|
|
36
|
-
#
|
|
37
|
-
# # find all servers in the app role that are not exempted from
|
|
38
|
-
# # deployment
|
|
39
|
-
# servers = find_servers :roles => :app,
|
|
40
|
-
# :except => { :no_release => true }
|
|
41
|
-
#
|
|
42
|
-
# # returns the given hosts, translated to ServerDefinition objects
|
|
43
|
-
# servers = find_servers :hosts => "jamis@example.host.com"
|
|
44
|
-
def find_servers(options={})
|
|
45
|
-
return [] if options.key?(:hosts) && (options[:hosts].nil? || [] == options[:hosts])
|
|
46
|
-
return [] if options.key?(:roles) && (options[:roles].nil? || [] == options[:roles])
|
|
47
|
-
|
|
48
|
-
hosts = server_list_from(ENV['HOSTS'] || options[:hosts])
|
|
49
|
-
|
|
50
|
-
if hosts.any?
|
|
51
|
-
if options[:skip_hostfilter]
|
|
52
|
-
hosts.uniq
|
|
53
|
-
else
|
|
54
|
-
filter_server_list(hosts.uniq)
|
|
55
|
-
end
|
|
10
|
+
def add_host(host, properties={})
|
|
11
|
+
new_host = Server[host]
|
|
12
|
+
new_host.port = properties[:port] if properties.key?(:port)
|
|
13
|
+
# This matching logic must stay in sync with `Server#matches?`.
|
|
14
|
+
key = ServerKey.new(new_host.hostname, new_host.port)
|
|
15
|
+
existing = servers_by_key[key]
|
|
16
|
+
if existing
|
|
17
|
+
existing.user = new_host.user if new_host.user
|
|
18
|
+
existing.with(properties)
|
|
56
19
|
else
|
|
57
|
-
|
|
58
|
-
roles = roles & Array(options[:roles]) if preserve_roles && !options[:roles].nil?
|
|
59
|
-
|
|
60
|
-
only = options[:only] || {}
|
|
61
|
-
except = options[:except] || {}
|
|
62
|
-
|
|
63
|
-
servers = roles.inject([]) { |list, role| list.concat(self.roles[role]) }
|
|
64
|
-
servers = servers.select { |server| only.all? { |key,value| server.options[key] == value } }
|
|
65
|
-
servers = servers.reject { |server| except.any? { |key,value| server.options[key] == value } }
|
|
66
|
-
|
|
67
|
-
if options[:skip_hostfilter]
|
|
68
|
-
servers.uniq
|
|
69
|
-
else
|
|
70
|
-
filter_server_list(servers.uniq)
|
|
71
|
-
end
|
|
20
|
+
servers_by_key[key] = new_host.with(properties)
|
|
72
21
|
end
|
|
73
22
|
end
|
|
74
23
|
|
|
75
|
-
|
|
24
|
+
# rubocop:disable Security/MarshalLoad
|
|
25
|
+
def add_role(role, hosts, options={})
|
|
26
|
+
options_deepcopy = Marshal.dump(options.merge(roles: role))
|
|
27
|
+
Array(hosts).each { |host| add_host(host, Marshal.load(options_deepcopy)) }
|
|
28
|
+
end
|
|
29
|
+
# rubocop:enable Security/MarshalLoad
|
|
76
30
|
|
|
77
|
-
def
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
31
|
+
def roles_for(names)
|
|
32
|
+
options = extract_options(names)
|
|
33
|
+
s = Filter.new(:role, names).filter(servers_by_key.values)
|
|
34
|
+
s.select { |server| server.select?(options) }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def role_properties_for(rolenames)
|
|
38
|
+
roles = rolenames.to_set
|
|
39
|
+
rps = Set.new unless block_given?
|
|
40
|
+
roles_for(rolenames).each do |host|
|
|
41
|
+
host.roles.intersection(roles).each do |role|
|
|
42
|
+
[host.properties.fetch(role)].flatten(1).each do |props|
|
|
43
|
+
if block_given?
|
|
44
|
+
yield host, role, props
|
|
45
|
+
else
|
|
46
|
+
rps << (props || {}).merge(role: role, hostname: host.hostname)
|
|
47
|
+
end
|
|
89
48
|
end
|
|
90
|
-
end
|
|
91
|
-
servers.select { |server| filters.include?(server) }
|
|
49
|
+
end
|
|
92
50
|
end
|
|
51
|
+
block_given? ? nil : rps
|
|
93
52
|
end
|
|
94
53
|
|
|
95
|
-
def
|
|
96
|
-
hosts =
|
|
97
|
-
hosts
|
|
98
|
-
hosts.map { |s| String === s ? ServerDefinition.new(s.strip) : s }
|
|
54
|
+
def fetch_primary(role)
|
|
55
|
+
hosts = roles_for([role])
|
|
56
|
+
hosts.find(&:primary) || hosts.first
|
|
99
57
|
end
|
|
100
58
|
|
|
101
|
-
def
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
59
|
+
def each
|
|
60
|
+
servers_by_key.values.each { |server| yield server }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
ServerKey = Struct.new(:hostname, :port)
|
|
66
|
+
|
|
67
|
+
def servers_by_key
|
|
68
|
+
@servers_by_key ||= {}
|
|
109
69
|
end
|
|
110
70
|
|
|
111
|
-
def
|
|
112
|
-
|
|
71
|
+
def extract_options(array)
|
|
72
|
+
array.last.is_a?(::Hash) ? array.pop : {}
|
|
113
73
|
end
|
|
114
74
|
end
|
|
115
75
|
end
|