capistrano 3.7.0 → 3.17.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/.circleci/config.yml +113 -0
- 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 +13 -0
- data/CHANGELOG.md +1 -484
- data/CONTRIBUTING.md +2 -0
- data/DEVELOPMENT.md +5 -6
- data/Dangerfile +1 -54
- data/Gemfile +39 -3
- data/LICENSE.txt +1 -1
- data/README.md +10 -4
- data/RELEASING.md +3 -3
- data/Rakefile +13 -5
- data/UPGRADING-3.7.md +1 -12
- data/capistrano.gemspec +9 -8
- data/features/deploy.feature +34 -1
- data/features/installation.feature +8 -3
- data/features/step_definitions/assertions.rb +31 -3
- data/features/step_definitions/cap_commands.rb +10 -0
- data/features/step_definitions/setup.rb +37 -1
- data/features/subdirectory.feature +9 -0
- data/features/support/remote_command_helpers.rb +4 -0
- data/features/support/vagrant_helpers.rb +15 -8
- data/lib/Capfile +0 -4
- data/lib/capistrano/application.rb +11 -3
- data/lib/capistrano/configuration/filter.rb +1 -1
- data/lib/capistrano/configuration/host_filter.rb +1 -2
- data/lib/capistrano/configuration/question.rb +22 -3
- data/lib/capistrano/configuration/role_filter.rb +1 -2
- data/lib/capistrano/configuration/scm_resolver.rb +8 -3
- data/lib/capistrano/configuration/server.rb +1 -0
- data/lib/capistrano/configuration/servers.rb +16 -8
- data/lib/capistrano/configuration/variables.rb +2 -2
- data/lib/capistrano/configuration.rb +7 -3
- data/lib/capistrano/defaults.rb +1 -1
- data/lib/capistrano/doctor/servers_doctor.rb +1 -1
- data/lib/capistrano/doctor/variables_doctor.rb +12 -3
- data/lib/capistrano/dsl/paths.rb +3 -16
- data/lib/capistrano/dsl.rb +10 -2
- data/lib/capistrano/i18n.rb +7 -2
- data/lib/capistrano/immutable_task.rb +3 -2
- data/lib/capistrano/scm/git.rb +34 -7
- data/lib/capistrano/scm/hg.rb +8 -1
- data/lib/capistrano/scm/svn.rb +9 -0
- data/lib/capistrano/scm/tasks/git.rake +10 -9
- data/lib/capistrano/scm/tasks/hg.rake +1 -1
- data/lib/capistrano/tasks/deploy.rake +22 -10
- data/lib/capistrano/templates/deploy.rb.erb +10 -4
- 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/filter_spec.rb +1 -1
- 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 +47 -11
- data/spec/lib/capistrano/configuration/role_filter_spec.rb +2 -2
- data/spec/lib/capistrano/configuration/scm_resolver_spec.rb +55 -0
- data/spec/lib/capistrano/configuration/server_spec.rb +1 -1
- data/spec/lib/capistrano/configuration/servers_spec.rb +6 -5
- data/spec/lib/capistrano/configuration_spec.rb +2 -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 +9 -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 +48 -7
- 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 +54 -1
- data/spec/lib/capistrano/scm/hg_spec.rb +6 -1
- data/spec/lib/capistrano/scm/svn_spec.rb +21 -0
- data/spec/lib/capistrano/version_validator_spec.rb +23 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/support/Vagrantfile +1 -1
- data/spec/support/test_app.rb +28 -14
- metadata +20 -80
- data/.travis.yml +0 -26
|
@@ -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,12 +49,28 @@ module Capistrano
|
|
|
46
49
|
end
|
|
47
50
|
|
|
48
51
|
def question
|
|
49
|
-
|
|
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
|
|
50
61
|
end
|
|
51
62
|
|
|
52
63
|
def echo?
|
|
53
64
|
(options || {}).fetch(:echo, true)
|
|
54
65
|
end
|
|
66
|
+
|
|
67
|
+
def stdin
|
|
68
|
+
(options || {}).fetch(:stdin, $stdin)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def prompt
|
|
72
|
+
(options || {}).fetch(:prompt, nil)
|
|
73
|
+
end
|
|
55
74
|
end
|
|
56
75
|
end
|
|
57
76
|
end
|
|
@@ -29,8 +29,12 @@ module Capistrano
|
|
|
29
29
|
set(:scm, :git) if using_default_scm?
|
|
30
30
|
|
|
31
31
|
print_deprecation_warnings_if_applicable
|
|
32
|
+
|
|
32
33
|
# Note that `scm_plugin_installed?` comes from Capistrano::DSL
|
|
33
|
-
|
|
34
|
+
if scm_plugin_installed?
|
|
35
|
+
delete(:scm)
|
|
36
|
+
return
|
|
37
|
+
end
|
|
34
38
|
|
|
35
39
|
if built_in_scm_name?
|
|
36
40
|
load_built_in_scm
|
|
@@ -108,7 +112,8 @@ module Capistrano
|
|
|
108
112
|
$stderr.puts(<<-MESSAGE)
|
|
109
113
|
[Deprecation Notice] `set :scm, #{scm_name.inspect}` is deprecated.
|
|
110
114
|
To ensure your project is compatible with future versions of Capistrano,
|
|
111
|
-
remove the :scm setting and instead add these lines to your Capfile
|
|
115
|
+
remove the :scm setting and instead add these lines to your Capfile after
|
|
116
|
+
`require "capistrano/deploy"`:
|
|
112
117
|
|
|
113
118
|
require "capistrano/scm/#{scm_name}"
|
|
114
119
|
install_plugin #{built_in_scm_plugin_class_name}
|
|
@@ -120,7 +125,7 @@ MESSAGE
|
|
|
120
125
|
$stderr.puts(<<-MESSAGE)
|
|
121
126
|
[Deprecation Notice] Future versions of Capistrano will not load the Git SCM
|
|
122
127
|
plugin by default. To silence this deprecation warning, add the following to
|
|
123
|
-
your Capfile
|
|
128
|
+
your Capfile after `require "capistrano/deploy"`:
|
|
124
129
|
|
|
125
130
|
require "capistrano/scm/git"
|
|
126
131
|
install_plugin Capistrano::SCM::Git
|
|
@@ -9,22 +9,28 @@ module Capistrano
|
|
|
9
9
|
|
|
10
10
|
def add_host(host, properties={})
|
|
11
11
|
new_host = Server[host]
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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)
|
|
15
19
|
else
|
|
16
|
-
|
|
20
|
+
servers_by_key[key] = new_host.with(properties)
|
|
17
21
|
end
|
|
18
22
|
end
|
|
19
23
|
|
|
24
|
+
# rubocop:disable Security/MarshalLoad
|
|
20
25
|
def add_role(role, hosts, options={})
|
|
21
26
|
options_deepcopy = Marshal.dump(options.merge(roles: role))
|
|
22
27
|
Array(hosts).each { |host| add_host(host, Marshal.load(options_deepcopy)) }
|
|
23
28
|
end
|
|
29
|
+
# rubocop:enable Security/MarshalLoad
|
|
24
30
|
|
|
25
31
|
def roles_for(names)
|
|
26
32
|
options = extract_options(names)
|
|
27
|
-
s = Filter.new(:role, names).filter(
|
|
33
|
+
s = Filter.new(:role, names).filter(servers_by_key.values)
|
|
28
34
|
s.select { |server| server.select?(options) }
|
|
29
35
|
end
|
|
30
36
|
|
|
@@ -51,13 +57,15 @@ module Capistrano
|
|
|
51
57
|
end
|
|
52
58
|
|
|
53
59
|
def each
|
|
54
|
-
|
|
60
|
+
servers_by_key.values.each { |server| yield server }
|
|
55
61
|
end
|
|
56
62
|
|
|
57
63
|
private
|
|
58
64
|
|
|
59
|
-
|
|
60
|
-
|
|
65
|
+
ServerKey = Struct.new(:hostname, :port)
|
|
66
|
+
|
|
67
|
+
def servers_by_key
|
|
68
|
+
@servers_by_key ||= {}
|
|
61
69
|
end
|
|
62
70
|
|
|
63
71
|
def extract_options(array)
|
|
@@ -35,7 +35,7 @@ module Capistrano
|
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
def set(key, value=nil, &block)
|
|
38
|
-
@trusted_keys << key if trusted?
|
|
38
|
+
@trusted_keys << key if trusted? && !@trusted_keys.include?(key)
|
|
39
39
|
remember_location(key)
|
|
40
40
|
values[key] = block || value
|
|
41
41
|
trace_set(key)
|
|
@@ -43,7 +43,7 @@ module Capistrano
|
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
def fetch(key, default=nil, &block)
|
|
46
|
-
fetched_keys << key
|
|
46
|
+
fetched_keys << key unless fetched_keys.include?(key)
|
|
47
47
|
peek(key, default, &block)
|
|
48
48
|
end
|
|
49
49
|
|
|
@@ -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)
|
data/lib/capistrano/defaults.rb
CHANGED
|
@@ -8,7 +8,7 @@ validate :application do |_key, value|
|
|
|
8
8
|
end
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
%i(git_strategy hg_strategy svn_strategy).each do |strategy|
|
|
12
12
|
validate(strategy) do |key, _value|
|
|
13
13
|
warn(
|
|
14
14
|
"[Deprecation Warning] #{key} is deprecated and will be removed in "\
|
|
@@ -5,9 +5,18 @@ 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 =
|
|
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
|
|
11
20
|
private_constant :WHITELIST
|
|
12
21
|
|
|
13
22
|
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
|
|
@@ -36,20 +36,7 @@ module Capistrano
|
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
def repo_url
|
|
39
|
-
|
|
40
|
-
require "uri"
|
|
41
|
-
if fetch(:git_http_username) && fetch(:git_http_password)
|
|
42
|
-
URI.parse(fetch(:repo_url)).tap do |repo_uri|
|
|
43
|
-
repo_uri.user = fetch(:git_http_username)
|
|
44
|
-
repo_uri.password = CGI.escape(fetch(:git_http_password))
|
|
45
|
-
end.to_s
|
|
46
|
-
elsif fetch(:git_http_username)
|
|
47
|
-
URI.parse(fetch(:repo_url)).tap do |repo_uri|
|
|
48
|
-
repo_uri.user = fetch(:git_http_username)
|
|
49
|
-
end.to_s
|
|
50
|
-
else
|
|
51
|
-
fetch(:repo_url)
|
|
52
|
-
end
|
|
39
|
+
fetch(:repo_url)
|
|
53
40
|
end
|
|
54
41
|
|
|
55
42
|
def repo_path
|
|
@@ -57,7 +44,7 @@ module Capistrano
|
|
|
57
44
|
end
|
|
58
45
|
|
|
59
46
|
def shared_path
|
|
60
|
-
deploy_path.join("shared")
|
|
47
|
+
deploy_path.join(fetch(:shared_directory, "shared"))
|
|
61
48
|
end
|
|
62
49
|
|
|
63
50
|
def revision_log
|
data/lib/capistrano/dsl.rb
CHANGED
|
@@ -19,15 +19,21 @@ module Capistrano
|
|
|
19
19
|
colors = SSHKit::Color.new($stderr)
|
|
20
20
|
$stderr.puts colors.colorize("Skipping task `#{task_name}'.", :yellow)
|
|
21
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,
|
|
22
|
+
$stderr.puts "If you really meant to run this task again, use invoke!(\"#{task_name}\")"
|
|
23
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
24
|
$stderr.puts colors.colorize("https://github.com/capistrano/capistrano/issues/1686", :red)
|
|
25
25
|
end
|
|
26
26
|
task.invoke(*args)
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
def invoke!(task_name, *args)
|
|
30
|
+
task = Rake::Task[task_name]
|
|
31
|
+
task.reenable
|
|
32
|
+
task.invoke(*args)
|
|
33
|
+
end
|
|
34
|
+
|
|
29
35
|
def t(key, options={})
|
|
30
|
-
I18n.t(key, options.merge(scope: :capistrano))
|
|
36
|
+
I18n.t(key, **options.merge(scope: :capistrano))
|
|
31
37
|
end
|
|
32
38
|
|
|
33
39
|
def scm
|
|
@@ -59,10 +65,12 @@ module Capistrano
|
|
|
59
65
|
VersionValidator.new(locked_version).verify
|
|
60
66
|
end
|
|
61
67
|
|
|
68
|
+
# rubocop:disable Security/MarshalLoad
|
|
62
69
|
def on(hosts, options={}, &block)
|
|
63
70
|
subset_copy = Marshal.dump(Configuration.env.filter(hosts))
|
|
64
71
|
SSHKit::Coordinator.new(Marshal.load(subset_copy)).each(options, &block)
|
|
65
72
|
end
|
|
73
|
+
# rubocop:enable Security/MarshalLoad
|
|
66
74
|
|
|
67
75
|
def run_locally(&block)
|
|
68
76
|
SSHKit::Backend::Local.new(&block).run
|
data/lib/capistrano/i18n.rb
CHANGED
|
@@ -10,9 +10,14 @@ en = {
|
|
|
10
10
|
finished: "Finished",
|
|
11
11
|
stage_not_set: "Stage not set, please call something such as `cap production deploy`, where production is a stage you have defined.",
|
|
12
12
|
written_file: "create %{file}",
|
|
13
|
-
question: "Please enter %{key}
|
|
13
|
+
question: "Please enter %{key}: ",
|
|
14
|
+
question_default: "Please enter %{key} (%{default_value}): ",
|
|
15
|
+
question_prompt: "%{key}: ",
|
|
16
|
+
question_prompt_default: "%{key} (%{default_value}): ",
|
|
14
17
|
keeping_releases: "Keeping %{keep_releases} of %{releases} deployed releases on %{host}",
|
|
15
|
-
skip_cleanup: "Skipping cleanup of
|
|
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}",
|
|
16
21
|
no_old_releases: "No old releases (keeping newest %{keep_releases}) on %{host}",
|
|
17
22
|
linked_file_does_not_exist: "linked file %{file} does not exist on %{host}",
|
|
18
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,13 +1,16 @@
|
|
|
1
1
|
require "capistrano/scm/plugin"
|
|
2
|
+
require "cgi"
|
|
3
|
+
require "securerandom"
|
|
4
|
+
require "shellwords"
|
|
5
|
+
require "uri"
|
|
2
6
|
|
|
3
7
|
class Capistrano::SCM::Git < Capistrano::SCM::Plugin
|
|
4
8
|
def set_defaults
|
|
5
9
|
set_if_empty :git_shallow_clone, false
|
|
6
10
|
set_if_empty :git_wrapper_path, lambda {
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
|
|
10
|
-
"#{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"
|
|
11
14
|
}
|
|
12
15
|
set_if_empty :git_environmental_variables, lambda {
|
|
13
16
|
{
|
|
@@ -15,6 +18,8 @@ class Capistrano::SCM::Git < Capistrano::SCM::Plugin
|
|
|
15
18
|
git_ssh: fetch(:git_wrapper_path)
|
|
16
19
|
}
|
|
17
20
|
}
|
|
21
|
+
set_if_empty :git_max_concurrent_connections, 10
|
|
22
|
+
set_if_empty :git_wait_interval, 0
|
|
18
23
|
end
|
|
19
24
|
|
|
20
25
|
def register_hooks
|
|
@@ -32,18 +37,21 @@ class Capistrano::SCM::Git < Capistrano::SCM::Plugin
|
|
|
32
37
|
end
|
|
33
38
|
|
|
34
39
|
def check_repo_is_reachable
|
|
35
|
-
git :'ls-remote
|
|
40
|
+
git :'ls-remote', git_repo_url, "HEAD"
|
|
36
41
|
end
|
|
37
42
|
|
|
38
43
|
def clone_repo
|
|
39
44
|
if (depth = fetch(:git_shallow_clone))
|
|
40
|
-
git :clone, "--mirror", "--depth", depth, "--no-single-branch",
|
|
45
|
+
git :clone, "--mirror", "--depth", depth, "--no-single-branch", git_repo_url, repo_path.to_s
|
|
41
46
|
else
|
|
42
|
-
git :clone, "--mirror",
|
|
47
|
+
git :clone, "--mirror", git_repo_url, repo_path.to_s
|
|
43
48
|
end
|
|
44
49
|
end
|
|
45
50
|
|
|
46
51
|
def update_mirror
|
|
52
|
+
# Update the origin URL if necessary.
|
|
53
|
+
git :remote, "set-url", "origin", git_repo_url
|
|
54
|
+
|
|
47
55
|
# Note: Requires git version 1.9 or greater
|
|
48
56
|
if (depth = fetch(:git_shallow_clone))
|
|
49
57
|
git :fetch, "--depth", depth, "origin", fetch(:branch)
|
|
@@ -52,6 +60,10 @@ class Capistrano::SCM::Git < Capistrano::SCM::Plugin
|
|
|
52
60
|
end
|
|
53
61
|
end
|
|
54
62
|
|
|
63
|
+
def verify_commit
|
|
64
|
+
git :"verify-commit", fetch_revision
|
|
65
|
+
end
|
|
66
|
+
|
|
55
67
|
def archive_to_release_path
|
|
56
68
|
if (tree = fetch(:repo_tree))
|
|
57
69
|
tree = tree.slice %r#^/?(.*?)/?$#, 1
|
|
@@ -70,4 +82,19 @@ class Capistrano::SCM::Git < Capistrano::SCM::Plugin
|
|
|
70
82
|
args.unshift :git
|
|
71
83
|
backend.execute(*args)
|
|
72
84
|
end
|
|
85
|
+
|
|
86
|
+
def git_repo_url
|
|
87
|
+
if fetch(:git_http_username) && fetch(:git_http_password)
|
|
88
|
+
URI.parse(repo_url).tap do |repo_uri|
|
|
89
|
+
repo_uri.user = fetch(:git_http_username)
|
|
90
|
+
repo_uri.password = CGI.escape(fetch(:git_http_password))
|
|
91
|
+
end.to_s
|
|
92
|
+
elsif fetch(:git_http_username)
|
|
93
|
+
URI.parse(repo_url).tap do |repo_uri|
|
|
94
|
+
repo_uri.user = fetch(:git_http_username)
|
|
95
|
+
end.to_s
|
|
96
|
+
else
|
|
97
|
+
repo_url
|
|
98
|
+
end
|
|
99
|
+
end
|
|
73
100
|
end
|
data/lib/capistrano/scm/hg.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require "capistrano/scm/plugin"
|
|
2
|
+
require "securerandom"
|
|
2
3
|
|
|
3
4
|
class Capistrano::SCM::Hg < Capistrano::SCM::Plugin
|
|
4
5
|
def register_hooks
|
|
@@ -36,7 +37,13 @@ class Capistrano::SCM::Hg < Capistrano::SCM::Plugin
|
|
|
36
37
|
if (tree = fetch(:repo_tree))
|
|
37
38
|
tree = tree.slice %r#^/?(.*?)/?$#, 1
|
|
38
39
|
components = tree.split("/").size
|
|
39
|
-
|
|
40
|
+
temp_tar = "#{fetch(:tmp_dir)}/#{SecureRandom.hex(10)}.tar"
|
|
41
|
+
|
|
42
|
+
hg "archive -p . -I", tree, "--rev", fetch(:branch), temp_tar
|
|
43
|
+
|
|
44
|
+
backend.execute :mkdir, "-p", release_path
|
|
45
|
+
backend.execute :tar, "-x --strip-components #{components} -f", temp_tar, "-C", release_path
|
|
46
|
+
backend.execute :rm, temp_tar
|
|
40
47
|
else
|
|
41
48
|
hg "archive", release_path, "--rev", fetch(:branch)
|
|
42
49
|
end
|
data/lib/capistrano/scm/svn.rb
CHANGED
|
@@ -34,6 +34,9 @@ class Capistrano::SCM::Svn < Capistrano::SCM::Plugin
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def update_mirror
|
|
37
|
+
# Switch the repository URL if necessary.
|
|
38
|
+
repo_mirror_url = fetch_repo_mirror_url
|
|
39
|
+
svn :switch, repo_url unless repo_mirror_url == repo_url
|
|
37
40
|
svn :update
|
|
38
41
|
end
|
|
39
42
|
|
|
@@ -44,4 +47,10 @@ class Capistrano::SCM::Svn < Capistrano::SCM::Plugin
|
|
|
44
47
|
def fetch_revision
|
|
45
48
|
backend.capture(:svnversion, repo_path.to_s)
|
|
46
49
|
end
|
|
50
|
+
|
|
51
|
+
def fetch_repo_mirror_url
|
|
52
|
+
backend.capture(:svn, :info, repo_path.to_s).each_line do |line|
|
|
53
|
+
return $1 if /\AURL: (.*)\n\z/ =~ line
|
|
54
|
+
end
|
|
55
|
+
end
|
|
47
56
|
end
|
|
@@ -4,17 +4,17 @@ 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
|
|
8
|
-
execute :mkdir, "-p", File.dirname(fetch(:git_wrapper_path))
|
|
9
|
-
upload! StringIO.new("#!/bin/sh -e\nexec /usr/bin/ssh -o PasswordAuthentication=no -o StrictHostKeyChecking=no \"$@\"\n"), fetch(:git_wrapper_path)
|
|
10
|
-
execute :chmod, "700", fetch(:git_wrapper_path)
|
|
7
|
+
on release_roles(:all), in: :groups, limit: fetch(:git_max_concurrent_connections), wait: fetch(:git_wait_interval) do
|
|
8
|
+
execute :mkdir, "-p", File.dirname(fetch(:git_wrapper_path)).shellescape
|
|
9
|
+
upload! StringIO.new("#!/bin/sh -e\nexec /usr/bin/env ssh -o PasswordAuthentication=no -o StrictHostKeyChecking=no \"$@\"\n"), fetch(:git_wrapper_path)
|
|
10
|
+
execute :chmod, "700", fetch(:git_wrapper_path).shellescape
|
|
11
11
|
end
|
|
12
12
|
end
|
|
13
13
|
|
|
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,10 +38,11 @@ namespace :git do
|
|
|
38
38
|
|
|
39
39
|
desc "Update the repo mirror to reflect the origin state"
|
|
40
40
|
task update: :'git:clone' do
|
|
41
|
-
on release_roles
|
|
41
|
+
on release_roles(:all), in: :groups, limit: fetch(:git_max_concurrent_connections), wait: fetch(:git_wait_interval) do
|
|
42
42
|
within repo_path do
|
|
43
43
|
with fetch(:git_environmental_variables) do
|
|
44
44
|
git_plugin.update_mirror
|
|
45
|
+
git_plugin.verify_commit if fetch(:git_verify_commit)
|
|
45
46
|
end
|
|
46
47
|
end
|
|
47
48
|
end
|
|
@@ -49,7 +50,7 @@ namespace :git do
|
|
|
49
50
|
|
|
50
51
|
desc "Copy repo to releases"
|
|
51
52
|
task create_release: :'git:update' do
|
|
52
|
-
on release_roles
|
|
53
|
+
on release_roles(:all), in: :groups, limit: fetch(:git_max_concurrent_connections), wait: fetch(:git_wait_interval) do
|
|
53
54
|
with fetch(:git_environmental_variables) do
|
|
54
55
|
within repo_path do
|
|
55
56
|
execute :mkdir, "-p", release_path
|
|
@@ -61,7 +62,7 @@ namespace :git do
|
|
|
61
62
|
|
|
62
63
|
desc "Determine the revision that will be deployed"
|
|
63
64
|
task :set_current_revision do
|
|
64
|
-
on release_roles
|
|
65
|
+
on release_roles(:all), in: :groups, limit: fetch(:git_max_concurrent_connections), wait: fetch(:git_wait_interval) do
|
|
65
66
|
within repo_path do
|
|
66
67
|
with fetch(:git_environmental_variables) do
|
|
67
68
|
set :current_revision, git_plugin.fetch_revision
|
|
@@ -149,16 +149,28 @@ namespace :deploy do
|
|
|
149
149
|
task :cleanup do
|
|
150
150
|
on release_roles :all do |host|
|
|
151
151
|
releases = capture(:ls, "-x", releases_path).split
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
152
|
+
valid, invalid = releases.partition { |e| /^\d{14}$/ =~ e }
|
|
153
|
+
|
|
154
|
+
warn t(:skip_cleanup, host: host.to_s) if invalid.any?
|
|
155
|
+
|
|
156
|
+
if valid.count >= fetch(:keep_releases)
|
|
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))).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
|
|
157
170
|
if directories.any?
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
end
|
|
161
|
-
execute :rm, "-rf", directories_str
|
|
171
|
+
directories.each_slice(100) do |directories_batch|
|
|
172
|
+
execute :rm, "-rf", *directories_batch
|
|
173
|
+
end
|
|
162
174
|
else
|
|
163
175
|
info t(:no_old_releases, host: host.to_s, keep_releases: fetch(:keep_releases))
|
|
164
176
|
end
|
|
@@ -227,7 +239,7 @@ namespace :deploy do
|
|
|
227
239
|
task :set_current_revision do
|
|
228
240
|
on release_roles(:all) do
|
|
229
241
|
within release_path do
|
|
230
|
-
execute :echo, "\"#{fetch(:current_revision)}\"
|
|
242
|
+
execute :echo, "\"#{fetch(:current_revision)}\" > REVISION"
|
|
231
243
|
end
|
|
232
244
|
end
|
|
233
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,13 +21,19 @@ 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", 'config/master.key'
|
|
25
25
|
|
|
26
26
|
# Default value for linked_dirs is []
|
|
27
|
-
# append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "public/system"
|
|
27
|
+
# append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "tmp/webpacker", "public/system", "vendor", "storage"
|
|
28
28
|
|
|
29
29
|
# Default value for default_env is {}
|
|
30
30
|
# set :default_env, { path: "/opt/ruby/bin:$PATH" }
|
|
31
31
|
|
|
32
|
+
# Default value for local_user is ENV['USER']
|
|
33
|
+
# set :local_user, -> { `git config user.name`.chomp }
|
|
34
|
+
|
|
32
35
|
# Default value for keep_releases is 5
|
|
33
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