capistrano 3.2.1 → 3.3.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +5 -3
- data/CHANGELOG.md +92 -2
- data/Gemfile +1 -5
- data/README.md +84 -3
- data/Rakefile +5 -1
- data/capistrano.gemspec +5 -1
- data/features/configuration.feature +9 -7
- data/features/deploy.feature +9 -8
- data/features/step_definitions/assertions.rb +15 -17
- data/features/step_definitions/cap_commands.rb +1 -1
- data/features/step_definitions/setup.rb +11 -7
- data/features/support/env.rb +8 -9
- data/features/support/remote_command_helpers.rb +2 -3
- data/features/support/vagrant_helpers.rb +35 -0
- data/lib/capistrano/all.rb +2 -1
- data/lib/capistrano/application.rb +52 -7
- data/lib/capistrano/configuration.rb +39 -10
- data/lib/capistrano/configuration/filter.rb +56 -0
- data/lib/capistrano/configuration/question.rb +23 -11
- data/lib/capistrano/configuration/server.rb +14 -5
- data/lib/capistrano/configuration/servers.rb +12 -29
- data/lib/capistrano/defaults.rb +11 -9
- data/lib/capistrano/deploy.rb +1 -0
- data/lib/capistrano/dsl.rb +13 -2
- data/lib/capistrano/dsl/env.rb +6 -2
- data/lib/capistrano/dsl/task_enhancements.rb +5 -3
- data/lib/capistrano/git.rb +8 -2
- data/lib/capistrano/hg.rb +7 -1
- data/lib/capistrano/svn.rb +2 -2
- data/lib/capistrano/tasks/deploy.rake +12 -10
- data/lib/capistrano/tasks/git.rake +1 -1
- data/lib/capistrano/tasks/install.rake +17 -14
- data/lib/capistrano/templates/Capfile +6 -4
- data/lib/capistrano/templates/deploy.rb.erb +5 -15
- data/lib/capistrano/upload_task.rb +9 -0
- data/lib/capistrano/version.rb +1 -1
- data/spec/integration/dsl_spec.rb +129 -10
- data/spec/lib/capistrano/application_spec.rb +24 -6
- data/spec/lib/capistrano/configuration/filter_spec.rb +105 -0
- data/spec/lib/capistrano/configuration/question_spec.rb +18 -12
- data/spec/lib/capistrano/configuration/server_spec.rb +19 -19
- data/spec/lib/capistrano/configuration/servers_spec.rb +101 -20
- data/spec/lib/capistrano/configuration_spec.rb +24 -3
- data/spec/lib/capistrano/dsl/task_enhancements_spec.rb +88 -0
- data/spec/lib/capistrano/dsl_spec.rb +2 -13
- data/spec/lib/capistrano/git_spec.rb +15 -4
- data/spec/lib/capistrano/hg_spec.rb +13 -2
- data/spec/lib/capistrano/scm_spec.rb +3 -3
- data/spec/lib/capistrano/svn_spec.rb +11 -1
- data/spec/lib/capistrano/upload_task_spec.rb +19 -0
- data/spec/lib/capistrano/version_validator_spec.rb +4 -4
- data/spec/spec_helper.rb +2 -1
- data/spec/support/Vagrantfile +1 -1
- data/spec/support/test_app.rb +2 -0
- metadata +45 -26
- data/lib/capistrano/configuration/servers/host_filter.rb +0 -82
- data/lib/capistrano/configuration/servers/role_filter.rb +0 -86
- data/spec/lib/capistrano/configuration/servers/host_filter_spec.rb +0 -84
- data/spec/lib/capistrano/configuration/servers/role_filter_spec.rb +0 -140
@@ -1,26 +1,29 @@
|
|
1
1
|
require 'set'
|
2
|
-
|
3
|
-
|
2
|
+
require 'capistrano/configuration'
|
3
|
+
require 'capistrano/configuration/filter'
|
4
|
+
|
4
5
|
module Capistrano
|
5
6
|
class Configuration
|
6
7
|
class Servers
|
7
8
|
include Enumerable
|
8
9
|
|
9
10
|
def add_host(host, properties={})
|
10
|
-
servers.add server(host).with(properties)
|
11
|
+
servers.add server(host, properties).with(properties)
|
11
12
|
end
|
12
13
|
|
13
14
|
def add_role(role, hosts, options={})
|
14
|
-
|
15
|
+
options_deepcopy = Marshal.dump(options.merge(roles: role))
|
16
|
+
Array(hosts).each { |host| add_host(host, Marshal.load(options_deepcopy)) }
|
15
17
|
end
|
16
18
|
|
17
19
|
def roles_for(names)
|
18
20
|
options = extract_options(names)
|
19
|
-
|
21
|
+
s = Filter.new(:role, names).filter(servers)
|
22
|
+
s.select { |server| server.select?(options) }
|
20
23
|
end
|
21
24
|
|
22
25
|
def fetch_primary(role)
|
23
|
-
hosts =
|
26
|
+
hosts = roles_for([role])
|
24
27
|
hosts.find(&:primary) || hosts.first
|
25
28
|
end
|
26
29
|
|
@@ -30,29 +33,9 @@ module Capistrano
|
|
30
33
|
|
31
34
|
private
|
32
35
|
|
33
|
-
def server(host)
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
def fetch(role)
|
38
|
-
servers.find_all { |server| server.has_role? role}
|
39
|
-
end
|
40
|
-
|
41
|
-
def fetch_roles(required, options)
|
42
|
-
filter_roles = RoleFilter.for(required, available_roles)
|
43
|
-
HostFilter.for(select(servers_with_roles(filter_roles), options))
|
44
|
-
end
|
45
|
-
|
46
|
-
def servers_with_roles(roles)
|
47
|
-
roles.flat_map { |role| fetch role }.uniq
|
48
|
-
end
|
49
|
-
|
50
|
-
def select(servers, options)
|
51
|
-
servers.select { |server| server.select?(options) }
|
52
|
-
end
|
53
|
-
|
54
|
-
def available_roles
|
55
|
-
servers.flat_map { |server| server.roles_array }.uniq
|
36
|
+
def server(host, properties)
|
37
|
+
new_host = Server[host].with(properties)
|
38
|
+
servers.find { |server| server.matches? new_host } || new_host
|
56
39
|
end
|
57
40
|
|
58
41
|
def servers
|
data/lib/capistrano/defaults.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
set_if_empty :scm, :git
|
2
|
+
set_if_empty :branch, :master
|
3
|
+
set_if_empty :deploy_to, -> { "/var/www/#{fetch(:application)}" }
|
4
|
+
set_if_empty :tmp_dir, "/tmp"
|
5
5
|
|
6
|
-
|
7
|
-
|
6
|
+
set_if_empty :default_env, {}
|
7
|
+
set_if_empty :keep_releases, 5
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
set_if_empty :format, :pretty
|
10
|
+
set_if_empty :log_level, :debug
|
11
11
|
|
12
|
-
|
12
|
+
set_if_empty :pty, false
|
13
|
+
|
14
|
+
set_if_empty :local_user, -> { Etc.getlogin }
|
data/lib/capistrano/deploy.rb
CHANGED
data/lib/capistrano/dsl.rb
CHANGED
@@ -3,6 +3,7 @@ require 'capistrano/dsl/task_enhancements'
|
|
3
3
|
require 'capistrano/dsl/paths'
|
4
4
|
require 'capistrano/dsl/stages'
|
5
5
|
require 'capistrano/dsl/env'
|
6
|
+
require 'capistrano/configuration/filter'
|
6
7
|
|
7
8
|
module Capistrano
|
8
9
|
module DSL
|
@@ -33,7 +34,7 @@ module Capistrano
|
|
33
34
|
branch: fetch(:branch),
|
34
35
|
user: local_user,
|
35
36
|
sha: fetch(:current_revision),
|
36
|
-
release: release_timestamp)
|
37
|
+
release: fetch(:release_timestamp))
|
37
38
|
)
|
38
39
|
end
|
39
40
|
|
@@ -42,12 +43,22 @@ module Capistrano
|
|
42
43
|
end
|
43
44
|
|
44
45
|
def local_user
|
45
|
-
|
46
|
+
fetch(:local_user)
|
46
47
|
end
|
47
48
|
|
48
49
|
def lock(locked_version)
|
49
50
|
VersionValidator.new(locked_version).verify
|
50
51
|
end
|
52
|
+
|
53
|
+
def on(hosts, options={}, &block)
|
54
|
+
subset = Configuration.env.filter hosts
|
55
|
+
SSHKit::Coordinator.new(subset).each(options, &block)
|
56
|
+
end
|
57
|
+
|
58
|
+
def run_locally(&block)
|
59
|
+
SSHKit::Backend::Local.new(&block).run
|
60
|
+
end
|
61
|
+
|
51
62
|
end
|
52
63
|
end
|
53
64
|
self.extend Capistrano::DSL
|
data/lib/capistrano/dsl/env.rb
CHANGED
@@ -23,12 +23,16 @@ module Capistrano
|
|
23
23
|
env.set(key, value)
|
24
24
|
end
|
25
25
|
|
26
|
+
def set_if_empty(key, value)
|
27
|
+
env.set_if_empty(key, value)
|
28
|
+
end
|
29
|
+
|
26
30
|
def delete(key)
|
27
31
|
env.delete(key)
|
28
32
|
end
|
29
33
|
|
30
|
-
def ask(key, value)
|
31
|
-
env.ask(key, value)
|
34
|
+
def ask(key, value, options={})
|
35
|
+
env.ask(key, value, options)
|
32
36
|
end
|
33
37
|
|
34
38
|
def role(name, servers, options={})
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'capistrano/upload_task'
|
2
|
+
|
1
3
|
module Capistrano
|
2
4
|
module TaskEnhancements
|
3
5
|
def before(task, prerequisite, *args, &block)
|
@@ -19,12 +21,12 @@ module Capistrano
|
|
19
21
|
end
|
20
22
|
|
21
23
|
def define_remote_file_task(task, target_roles)
|
22
|
-
|
24
|
+
Capistrano::UploadTask.define_task(task) do |t|
|
23
25
|
prerequisite_file = t.prerequisites.first
|
24
26
|
file = shared_path.join(t.name)
|
25
27
|
|
26
28
|
on roles(target_roles) do
|
27
|
-
unless test "[ -f #{file} ]"
|
29
|
+
unless test "[ -f #{file.to_s.shellescape} ]"
|
28
30
|
info "Uploading #{prerequisite_file} to #{file}"
|
29
31
|
upload! File.open(prerequisite_file), file
|
30
32
|
end
|
@@ -51,7 +53,7 @@ module Capistrano
|
|
51
53
|
end
|
52
54
|
|
53
55
|
def exit_deploy_because_of_exception(ex)
|
54
|
-
warn t(:deploy_failed, ex: ex.
|
56
|
+
warn t(:deploy_failed, ex: ex.message)
|
55
57
|
invoke 'deploy:failed'
|
56
58
|
exit(false)
|
57
59
|
end
|
data/lib/capistrano/git.rb
CHANGED
@@ -18,7 +18,7 @@ class Capistrano::Git < Capistrano::SCM
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def check
|
21
|
-
|
21
|
+
git :'ls-remote -h', repo_url
|
22
22
|
end
|
23
23
|
|
24
24
|
def clone
|
@@ -30,7 +30,13 @@ class Capistrano::Git < Capistrano::SCM
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def release
|
33
|
-
|
33
|
+
if tree = fetch(:repo_tree)
|
34
|
+
tree = tree.slice %r#^/?(.*?)/?$#, 1
|
35
|
+
components = tree.split('/').size
|
36
|
+
git :archive, fetch(:branch), tree, "| tar -x --strip-components #{components} -f - -C", release_path
|
37
|
+
else
|
38
|
+
git :archive, fetch(:branch), '| tar -x -f - -C', release_path
|
39
|
+
end
|
34
40
|
end
|
35
41
|
|
36
42
|
def fetch_revision
|
data/lib/capistrano/hg.rb
CHANGED
@@ -27,7 +27,13 @@ class Capistrano::Hg < Capistrano::SCM
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def release
|
30
|
-
|
30
|
+
if tree = fetch(:repo_tree)
|
31
|
+
tree = tree.slice %r#^/?(.*?)/?$#, 1
|
32
|
+
components = tree.split('/').size
|
33
|
+
hg "archive --type tgz -p . -I", tree, "--rev", fetch(:branch), "| tar -x --strip-components #{components} -f - -C", release_path
|
34
|
+
else
|
35
|
+
hg "archive", release_path, "--rev", fetch(:branch)
|
36
|
+
end
|
31
37
|
end
|
32
38
|
|
33
39
|
def fetch_revision
|
data/lib/capistrano/svn.rb
CHANGED
@@ -28,11 +28,11 @@ class Capistrano::Svn < Capistrano::SCM
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def release
|
31
|
-
svn :export, '.', release_path
|
31
|
+
svn :export, '--force', '.', release_path
|
32
32
|
end
|
33
33
|
|
34
34
|
def fetch_revision
|
35
|
-
context.capture(:
|
35
|
+
context.capture(:svnversion, repo_path)
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
namespace :deploy do
|
2
2
|
|
3
3
|
task :starting do
|
4
|
+
invoke 'metrics:collect'
|
4
5
|
invoke 'deploy:check'
|
5
6
|
invoke 'deploy:set_previous_revision'
|
6
7
|
end
|
@@ -44,7 +45,7 @@ namespace :deploy do
|
|
44
45
|
desc 'Check shared and release directories exist'
|
45
46
|
task :directories do
|
46
47
|
on release_roles :all do
|
47
|
-
execute :mkdir, '-
|
48
|
+
execute :mkdir, '-p', shared_path, releases_path
|
48
49
|
end
|
49
50
|
end
|
50
51
|
|
@@ -52,7 +53,7 @@ namespace :deploy do
|
|
52
53
|
task :linked_dirs do
|
53
54
|
next unless any? :linked_dirs
|
54
55
|
on release_roles :all do
|
55
|
-
execute :mkdir, '-
|
56
|
+
execute :mkdir, '-p', linked_dirs(shared_path)
|
56
57
|
end
|
57
58
|
end
|
58
59
|
|
@@ -60,7 +61,7 @@ namespace :deploy do
|
|
60
61
|
task :make_linked_dirs do
|
61
62
|
next unless any? :linked_files
|
62
63
|
on release_roles :all do |host|
|
63
|
-
execute :mkdir, '-
|
64
|
+
execute :mkdir, '-p', linked_file_dirs(shared_path)
|
64
65
|
end
|
65
66
|
end
|
66
67
|
|
@@ -82,8 +83,9 @@ namespace :deploy do
|
|
82
83
|
desc 'Symlink release to current'
|
83
84
|
task :release do
|
84
85
|
on release_roles :all do
|
85
|
-
|
86
|
-
execute :ln, '-s', release_path,
|
86
|
+
tmp_current_path = release_path.parent.join(current_path.basename)
|
87
|
+
execute :ln, '-s', release_path, tmp_current_path
|
88
|
+
execute :mv, tmp_current_path, current_path.parent
|
87
89
|
end
|
88
90
|
end
|
89
91
|
|
@@ -97,7 +99,7 @@ namespace :deploy do
|
|
97
99
|
task :linked_dirs do
|
98
100
|
next unless any? :linked_dirs
|
99
101
|
on release_roles :all do
|
100
|
-
execute :mkdir, '-
|
102
|
+
execute :mkdir, '-p', linked_dir_parents(release_path)
|
101
103
|
|
102
104
|
fetch(:linked_dirs).each do |dir|
|
103
105
|
target = release_path.join(dir)
|
@@ -116,7 +118,7 @@ namespace :deploy do
|
|
116
118
|
task :linked_files do
|
117
119
|
next unless any? :linked_files
|
118
120
|
on release_roles :all do
|
119
|
-
execute :mkdir, '-
|
121
|
+
execute :mkdir, '-p', linked_file_dirs(release_path)
|
120
122
|
|
121
123
|
fetch(:linked_files).each do |file|
|
122
124
|
target = release_path.join(file)
|
@@ -135,7 +137,7 @@ namespace :deploy do
|
|
135
137
|
desc 'Clean up old releases'
|
136
138
|
task :cleanup do
|
137
139
|
on release_roles :all do |host|
|
138
|
-
releases = capture(:ls, '-
|
140
|
+
releases = capture(:ls, '-xtr', releases_path).split
|
139
141
|
if releases.count >= fetch(:keep_releases)
|
140
142
|
info t(:keeping_releases, host: host.to_s, keep_releases: fetch(:keep_releases), releases: releases.count)
|
141
143
|
directories = (releases - releases.last(fetch(:keep_releases)))
|
@@ -154,7 +156,7 @@ namespace :deploy do
|
|
154
156
|
desc 'Remove and archive rolled-back release.'
|
155
157
|
task :cleanup_rollback do
|
156
158
|
on release_roles(:all) do
|
157
|
-
last_release = capture(:ls, '-
|
159
|
+
last_release = capture(:ls, '-xt', releases_path).split.first
|
158
160
|
last_release_path = releases_path.join(last_release)
|
159
161
|
if test "[ `readlink #{current_path}` != #{last_release_path} ]"
|
160
162
|
execute :tar, '-czf',
|
@@ -189,7 +191,7 @@ namespace :deploy do
|
|
189
191
|
|
190
192
|
task :rollback_release_path do
|
191
193
|
on release_roles(:all) do
|
192
|
-
releases = capture(:ls, '-
|
194
|
+
releases = capture(:ls, '-xt', releases_path).split
|
193
195
|
if releases.count < 2
|
194
196
|
error t(:cannot_rollback)
|
195
197
|
exit 1
|
@@ -14,25 +14,28 @@ task :install do
|
|
14
14
|
|
15
15
|
mkdir_p deploy_dir
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
f.write(ERB.new(template).result(binding))
|
29
|
-
puts I18n.t(:written_file, scope: :capistrano, file: file)
|
17
|
+
entries = [{template: deploy_rb, file: config_dir.join('deploy.rb')}]
|
18
|
+
entries += envs.split(',').map { |stage| {template: stage_rb, file: deploy_dir.join("#{stage}.rb")} }
|
19
|
+
|
20
|
+
entries.each do |entry|
|
21
|
+
if File.exists?(entry[:file])
|
22
|
+
warn "[skip] #{entry[:file]} already exists"
|
23
|
+
else
|
24
|
+
File.open(entry[:file], 'w+') do |f|
|
25
|
+
f.write(ERB.new(File.read(entry[:template])).result(binding))
|
26
|
+
puts I18n.t(:written_file, scope: :capistrano, file: entry[:file])
|
27
|
+
end
|
30
28
|
end
|
31
29
|
end
|
32
30
|
|
33
31
|
mkdir_p tasks_dir
|
34
32
|
|
35
|
-
|
33
|
+
if File.exists?('Capfile')
|
34
|
+
warn "[skip] Capfile already exists"
|
35
|
+
else
|
36
|
+
FileUtils.cp(capfile, 'Capfile')
|
37
|
+
puts I18n.t(:written_file, scope: :capistrano, file: 'Capfile')
|
38
|
+
end
|
36
39
|
|
37
40
|
|
38
41
|
puts I18n.t :capified, scope: :capistrano
|
@@ -1,10 +1,10 @@
|
|
1
|
-
# Load DSL and
|
1
|
+
# Load DSL and set up stages
|
2
2
|
require 'capistrano/setup'
|
3
3
|
|
4
|
-
#
|
4
|
+
# Include default deployment tasks
|
5
5
|
require 'capistrano/deploy'
|
6
6
|
|
7
|
-
#
|
7
|
+
# Include tasks from other gems included in your Gemfile
|
8
8
|
#
|
9
9
|
# For documentation on these, see for example:
|
10
10
|
#
|
@@ -13,6 +13,7 @@ require 'capistrano/deploy'
|
|
13
13
|
# https://github.com/capistrano/chruby
|
14
14
|
# https://github.com/capistrano/bundler
|
15
15
|
# https://github.com/capistrano/rails
|
16
|
+
# https://github.com/capistrano/passenger
|
16
17
|
#
|
17
18
|
# require 'capistrano/rvm'
|
18
19
|
# require 'capistrano/rbenv'
|
@@ -20,6 +21,7 @@ require 'capistrano/deploy'
|
|
20
21
|
# require 'capistrano/bundler'
|
21
22
|
# require 'capistrano/rails/assets'
|
22
23
|
# require 'capistrano/rails/migrations'
|
24
|
+
# require 'capistrano/passenger'
|
23
25
|
|
24
|
-
#
|
26
|
+
# Load custom tasks from `lib/capistrano/tasks' if you have any defined
|
25
27
|
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# config valid only for Capistrano
|
1
|
+
# config valid only for current version of Capistrano
|
2
2
|
lock '<%= Capistrano::VERSION %>'
|
3
3
|
|
4
4
|
set :application, 'my_app_name'
|
@@ -7,8 +7,8 @@ set :repo_url, 'git@example.com:me/my_repo.git'
|
|
7
7
|
# Default branch is :master
|
8
8
|
# ask :branch, proc { `git rev-parse --abbrev-ref HEAD`.chomp }.call
|
9
9
|
|
10
|
-
# Default deploy_to directory is /var/www/
|
11
|
-
# set :deploy_to, '/var/www/
|
10
|
+
# Default deploy_to directory is /var/www/my_app_name
|
11
|
+
# set :deploy_to, '/var/www/my_app_name'
|
12
12
|
|
13
13
|
# Default value for :scm is :git
|
14
14
|
# set :scm, :git
|
@@ -23,10 +23,10 @@ set :repo_url, 'git@example.com:me/my_repo.git'
|
|
23
23
|
# set :pty, true
|
24
24
|
|
25
25
|
# Default value for :linked_files is []
|
26
|
-
# set :linked_files,
|
26
|
+
# set :linked_files, fetch(:linked_files, []).push('config/database.yml')
|
27
27
|
|
28
28
|
# Default value for linked_dirs is []
|
29
|
-
# set :linked_dirs,
|
29
|
+
# set :linked_dirs, fetch(:linked_dirs, []).push('bin', 'log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system')
|
30
30
|
|
31
31
|
# Default value for default_env is {}
|
32
32
|
# set :default_env, { path: "/opt/ruby/bin:$PATH" }
|
@@ -36,16 +36,6 @@ set :repo_url, 'git@example.com:me/my_repo.git'
|
|
36
36
|
|
37
37
|
namespace :deploy do
|
38
38
|
|
39
|
-
desc 'Restart application'
|
40
|
-
task :restart do
|
41
|
-
on roles(:app), in: :sequence, wait: 5 do
|
42
|
-
# Your restart mechanism here, for example:
|
43
|
-
# execute :touch, release_path.join('tmp/restart.txt')
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
after :publishing, :restart
|
48
|
-
|
49
39
|
after :restart, :clear_cache do
|
50
40
|
on roles(:web), in: :groups, limit: 3, wait: 10 do
|
51
41
|
# Here we can do anything such as:
|