capistrano 3.2.1 → 3.3.3

Sign up to get free protection for your applications and to get access to all the features.
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: