capistrano 3.2.1 → 3.3.3

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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +5 -3
  3. data/CHANGELOG.md +92 -2
  4. data/Gemfile +1 -5
  5. data/README.md +84 -3
  6. data/Rakefile +5 -1
  7. data/capistrano.gemspec +5 -1
  8. data/features/configuration.feature +9 -7
  9. data/features/deploy.feature +9 -8
  10. data/features/step_definitions/assertions.rb +15 -17
  11. data/features/step_definitions/cap_commands.rb +1 -1
  12. data/features/step_definitions/setup.rb +11 -7
  13. data/features/support/env.rb +8 -9
  14. data/features/support/remote_command_helpers.rb +2 -3
  15. data/features/support/vagrant_helpers.rb +35 -0
  16. data/lib/capistrano/all.rb +2 -1
  17. data/lib/capistrano/application.rb +52 -7
  18. data/lib/capistrano/configuration.rb +39 -10
  19. data/lib/capistrano/configuration/filter.rb +56 -0
  20. data/lib/capistrano/configuration/question.rb +23 -11
  21. data/lib/capistrano/configuration/server.rb +14 -5
  22. data/lib/capistrano/configuration/servers.rb +12 -29
  23. data/lib/capistrano/defaults.rb +11 -9
  24. data/lib/capistrano/deploy.rb +1 -0
  25. data/lib/capistrano/dsl.rb +13 -2
  26. data/lib/capistrano/dsl/env.rb +6 -2
  27. data/lib/capistrano/dsl/task_enhancements.rb +5 -3
  28. data/lib/capistrano/git.rb +8 -2
  29. data/lib/capistrano/hg.rb +7 -1
  30. data/lib/capistrano/svn.rb +2 -2
  31. data/lib/capistrano/tasks/deploy.rake +12 -10
  32. data/lib/capistrano/tasks/git.rake +1 -1
  33. data/lib/capistrano/tasks/install.rake +17 -14
  34. data/lib/capistrano/templates/Capfile +6 -4
  35. data/lib/capistrano/templates/deploy.rb.erb +5 -15
  36. data/lib/capistrano/upload_task.rb +9 -0
  37. data/lib/capistrano/version.rb +1 -1
  38. data/spec/integration/dsl_spec.rb +129 -10
  39. data/spec/lib/capistrano/application_spec.rb +24 -6
  40. data/spec/lib/capistrano/configuration/filter_spec.rb +105 -0
  41. data/spec/lib/capistrano/configuration/question_spec.rb +18 -12
  42. data/spec/lib/capistrano/configuration/server_spec.rb +19 -19
  43. data/spec/lib/capistrano/configuration/servers_spec.rb +101 -20
  44. data/spec/lib/capistrano/configuration_spec.rb +24 -3
  45. data/spec/lib/capistrano/dsl/task_enhancements_spec.rb +88 -0
  46. data/spec/lib/capistrano/dsl_spec.rb +2 -13
  47. data/spec/lib/capistrano/git_spec.rb +15 -4
  48. data/spec/lib/capistrano/hg_spec.rb +13 -2
  49. data/spec/lib/capistrano/scm_spec.rb +3 -3
  50. data/spec/lib/capistrano/svn_spec.rb +11 -1
  51. data/spec/lib/capistrano/upload_task_spec.rb +19 -0
  52. data/spec/lib/capistrano/version_validator_spec.rb +4 -4
  53. data/spec/spec_helper.rb +2 -1
  54. data/spec/support/Vagrantfile +1 -1
  55. data/spec/support/test_app.rb +2 -0
  56. metadata +45 -26
  57. data/lib/capistrano/configuration/servers/host_filter.rb +0 -82
  58. data/lib/capistrano/configuration/servers/role_filter.rb +0 -86
  59. data/spec/lib/capistrano/configuration/servers/host_filter_spec.rb +0 -84
  60. data/spec/lib/capistrano/configuration/servers/role_filter_spec.rb +0 -140
@@ -1,26 +1,29 @@
1
1
  require 'set'
2
- require_relative 'servers/role_filter'
3
- require_relative 'servers/host_filter'
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
- Array(hosts).each { |host| add_host(host, options.merge(roles: role)) }
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
- fetch_roles(names, options)
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 = fetch(role)
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
- servers.find { |server| server.matches? Server[host] } || Server[host]
35
- end
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
@@ -1,12 +1,14 @@
1
- set :scm, :git
2
- set :branch, :master
3
- set :deploy_to, -> { "/var/www/#{fetch(:application)}" }
4
- set :tmp_dir, "/tmp"
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
- set :default_env, {}
7
- set :keep_releases, 5
6
+ set_if_empty :default_env, {}
7
+ set_if_empty :keep_releases, 5
8
8
 
9
- set :format, :pretty
10
- set :log_level, :debug
9
+ set_if_empty :format, :pretty
10
+ set_if_empty :log_level, :debug
11
11
 
12
- set :pty, false
12
+ set_if_empty :pty, false
13
+
14
+ set_if_empty :local_user, -> { Etc.getlogin }
@@ -1,3 +1,4 @@
1
1
  require 'capistrano/framework'
2
+ require 'capistrano-stats'
2
3
 
3
4
  load File.expand_path("../tasks/deploy.rake", __FILE__)
@@ -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
- Etc.getlogin
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
@@ -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
- Rake::Task.define_task(task) do |t|
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.inspect)
56
+ warn t(:deploy_failed, ex: ex.message)
55
57
  invoke 'deploy:failed'
56
58
  exit(false)
57
59
  end
@@ -18,7 +18,7 @@ class Capistrano::Git < Capistrano::SCM
18
18
  end
19
19
 
20
20
  def check
21
- test! :git, :'ls-remote -h', repo_url
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
- git :archive, fetch(:branch), '| tar -x -C', release_path
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
@@ -27,7 +27,13 @@ class Capistrano::Hg < Capistrano::SCM
27
27
  end
28
28
 
29
29
  def release
30
- hg "archive", release_path, "--rev", fetch(:branch)
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
@@ -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(:svn, "log -r HEAD -q | tail -n 2 | head -n 1 | sed s/\ \|.*/''/")
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, '-pv', shared_path, releases_path
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, '-pv', linked_dirs(shared_path)
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, '-pv', linked_file_dirs(shared_path)
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
- execute :rm, '-rf', current_path
86
- execute :ln, '-s', release_path, current_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, '-pv', linked_dir_parents(release_path)
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, '-pv', linked_file_dirs(release_path)
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, '-x', releases_path).split
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, '-xr', releases_path).split.first
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, '-xr', releases_path).split
194
+ releases = capture(:ls, '-xt', releases_path).split
193
195
  if releases.count < 2
194
196
  error t(:cannot_rollback)
195
197
  exit 1
@@ -25,7 +25,7 @@ namespace :git do
25
25
  fetch(:branch)
26
26
  on release_roles :all do
27
27
  with fetch(:git_environmental_variables) do
28
- exit 1 unless strategy.check
28
+ strategy.check
29
29
  end
30
30
  end
31
31
  end
@@ -14,25 +14,28 @@ task :install do
14
14
 
15
15
  mkdir_p deploy_dir
16
16
 
17
- template = File.read(deploy_rb)
18
- file = config_dir.join('deploy.rb')
19
- File.open(file, 'w+') do |f|
20
- f.write(ERB.new(template).result(binding))
21
- puts I18n.t(:written_file, scope: :capistrano, file: file)
22
- end
23
-
24
- template = File.read(stage_rb)
25
- envs.split(',').each do |stage|
26
- file = deploy_dir.join("#{stage}.rb")
27
- File.open(file, 'w+') do |f|
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
- FileUtils.cp(capfile, 'Capfile')
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 Setup Up Stages
1
+ # Load DSL and set up stages
2
2
  require 'capistrano/setup'
3
3
 
4
- # Includes default deployment tasks
4
+ # Include default deployment tasks
5
5
  require 'capistrano/deploy'
6
6
 
7
- # Includes tasks from other gems included in your Gemfile
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
- # Loads custom tasks from `lib/capistrano/tasks' if you have any defined.
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 3.1
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/my_app
11
- # set :deploy_to, '/var/www/my_app'
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, %w{config/database.yml}
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, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system}
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: