capistrano 3.6.1 → 3.7.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.github/issue_template.md +19 -0
  3. data/.github/pull_request_template.md +26 -0
  4. data/.gitignore +1 -0
  5. data/.travis.yml +5 -1
  6. data/CHANGELOG.md +27 -1
  7. data/DEVELOPMENT.md +7 -1
  8. data/README.md +3 -4
  9. data/capistrano.gemspec +1 -1
  10. data/features/support/vagrant_helpers.rb +3 -5
  11. data/lib/capistrano/all.rb +1 -0
  12. data/lib/capistrano/configuration.rb +12 -2
  13. data/lib/capistrano/configuration/host_filter.rb +1 -1
  14. data/lib/capistrano/configuration/plugin_installer.rb +20 -2
  15. data/lib/capistrano/configuration/role_filter.rb +1 -1
  16. data/lib/capistrano/configuration/scm_resolver.rb +144 -0
  17. data/lib/capistrano/configuration/validated_variables.rb +3 -4
  18. data/lib/capistrano/defaults.rb +3 -1
  19. data/lib/capistrano/doctor/variables_doctor.rb +1 -1
  20. data/lib/capistrano/dsl/env.rb +2 -9
  21. data/lib/capistrano/dsl/paths.rb +1 -1
  22. data/lib/capistrano/dsl/task_enhancements.rb +0 -8
  23. data/lib/capistrano/scm/git.rb +73 -0
  24. data/lib/capistrano/scm/hg.rb +48 -0
  25. data/lib/capistrano/scm/plugin.rb +13 -0
  26. data/lib/capistrano/scm/svn.rb +47 -0
  27. data/lib/capistrano/{tasks → scm/tasks}/git.rake +9 -24
  28. data/lib/capistrano/{tasks → scm/tasks}/hg.rake +11 -10
  29. data/lib/capistrano/{tasks → scm/tasks}/svn.rake +11 -10
  30. data/lib/capistrano/setup.rb +1 -1
  31. data/lib/capistrano/tasks/deploy.rake +0 -3
  32. data/lib/capistrano/templates/Capfile +18 -7
  33. data/lib/capistrano/templates/deploy.rb.erb +7 -10
  34. data/lib/capistrano/templates/stage.rb.erb +7 -7
  35. data/lib/capistrano/version.rb +1 -1
  36. data/lib/capistrano/version_validator.rb +2 -5
  37. data/spec/lib/capistrano/configuration/host_filter_spec.rb +5 -0
  38. data/spec/lib/capistrano/configuration/plugin_installer_spec.rb +98 -0
  39. data/spec/lib/capistrano/configuration/role_filter_spec.rb +17 -1
  40. data/spec/lib/capistrano/doctor/variables_doctor_spec.rb +0 -7
  41. data/spec/lib/capistrano/dsl/task_enhancements_spec.rb +0 -15
  42. data/spec/lib/capistrano/scm/git_spec.rb +131 -0
  43. data/spec/lib/capistrano/scm/hg_spec.rb +104 -0
  44. data/spec/lib/capistrano/scm/svn_spec.rb +116 -0
  45. data/spec/lib/capistrano/scm_spec.rb +1 -1
  46. metadata +23 -20
  47. data/features/remote_file_task.feature +0 -14
  48. data/issue_template.md +0 -21
  49. data/lib/capistrano/git.rb +0 -54
  50. data/lib/capistrano/hg.rb +0 -43
  51. data/lib/capistrano/svn.rb +0 -42
  52. data/spec/lib/capistrano/git_spec.rb +0 -109
  53. data/spec/lib/capistrano/hg_spec.rb +0 -90
  54. data/spec/lib/capistrano/svn_spec.rb +0 -105
@@ -91,10 +91,9 @@ module Capistrano
91
91
  end
92
92
 
93
93
  def assert_value_or_block_not_both(value, block)
94
- unless value.nil? || block.nil?
95
- raise Capistrano::ValidationError,
96
- "Value and block both passed to Configuration#set"
97
- end
94
+ return if value.nil? || block.nil?
95
+ raise Capistrano::ValidationError,
96
+ "Value and block both passed to Configuration#set"
98
97
  end
99
98
 
100
99
  class ValidatedQuestion < Question
@@ -18,7 +18,9 @@ end
18
18
  end
19
19
  end
20
20
 
21
- set_if_empty :scm, :git
21
+ # We use a special :_default_git value so that SCMResolver can tell whether the
22
+ # default has been replaced by the user via `set`.
23
+ set_if_empty :scm, Capistrano::Configuration::SCMResolver::DEFAULT_GIT
22
24
  set_if_empty :branch, "master"
23
25
  set_if_empty :deploy_to, -> { "/var/www/#{fetch(:application)}" }
24
26
  set_if_empty :tmp_dir, "/tmp"
@@ -7,7 +7,7 @@ module Capistrano
7
7
  class VariablesDoctor
8
8
  # These are keys that have no default values in Capistrano, but are
9
9
  # nonetheless expected to be set.
10
- WHITELIST = [:application, :repo_url, :git_strategy, :hg_strategy, :svn_strategy].freeze
10
+ WHITELIST = [:application, :repo_url].freeze
11
11
  private_constant :WHITELIST
12
12
 
13
13
  include Capistrano::Doctor::OutputHelpers
@@ -7,15 +7,8 @@ module Capistrano
7
7
  def_delegators :env,
8
8
  :configure_backend, :fetch, :set, :set_if_empty, :delete,
9
9
  :ask, :role, :server, :primary, :validate, :append,
10
- :remove, :dry_run?, :install_plugin
11
-
12
- def is_question?(key)
13
- env.is_question?(key)
14
- end
15
-
16
- def any?(key)
17
- env.any?(key)
18
- end
10
+ :remove, :dry_run?, :install_plugin, :any?, :is_question?,
11
+ :configure_scm, :scm_plugin_installed?
19
12
 
20
13
  def roles(*names)
21
14
  env.roles_for(names.flatten)
@@ -19,7 +19,7 @@ module Capistrano
19
19
  end
20
20
 
21
21
  def release_path
22
- fetch(:release_path, current_path)
22
+ fetch(:release_path) { current_path }
23
23
  end
24
24
 
25
25
  def set_release_path(timestamp=now)
@@ -17,14 +17,6 @@ module Capistrano
17
17
  end
18
18
  end
19
19
 
20
- def remote_file(task)
21
- warn("[Deprecation Warning] `remote_file` is deprecated and will be "\
22
- "removed in Capistrano 3.7.0")
23
-
24
- target_roles = task.delete(:roles) { :all }
25
- define_remote_file_task(task, target_roles)
26
- end
27
-
28
20
  def define_remote_file_task(task, target_roles)
29
21
  Capistrano::UploadTask.define_task(task) do |t|
30
22
  prerequisite_file = t.prerequisites.first
@@ -0,0 +1,73 @@
1
+ require "capistrano/scm/plugin"
2
+
3
+ class Capistrano::SCM::Git < Capistrano::SCM::Plugin
4
+ def set_defaults
5
+ set_if_empty :git_shallow_clone, false
6
+ set_if_empty :git_wrapper_path, lambda {
7
+ # Try to avoid permissions issues when multiple users deploy the same app
8
+ # by using different file names in the same dir for each deployer and stage.
9
+ suffix = [:application, :stage, :local_user].map { |key| fetch(key).to_s }.join("-").gsub(/\s+/, "-")
10
+ "#{fetch(:tmp_dir)}/git-ssh-#{suffix}.sh"
11
+ }
12
+ set_if_empty :git_environmental_variables, lambda {
13
+ {
14
+ git_askpass: "/bin/echo",
15
+ git_ssh: fetch(:git_wrapper_path)
16
+ }
17
+ }
18
+ end
19
+
20
+ def register_hooks
21
+ after "deploy:new_release_path", "git:create_release"
22
+ before "deploy:check", "git:check"
23
+ before "deploy:set_current_revision", "git:set_current_revision"
24
+ end
25
+
26
+ def define_tasks
27
+ eval_rakefile File.expand_path("../tasks/git.rake", __FILE__)
28
+ end
29
+
30
+ def repo_mirror_exists?
31
+ backend.test " [ -f #{repo_path}/HEAD ] "
32
+ end
33
+
34
+ def check_repo_is_reachable
35
+ git :'ls-remote --heads', repo_url
36
+ end
37
+
38
+ def clone_repo
39
+ if (depth = fetch(:git_shallow_clone))
40
+ git :clone, "--mirror", "--depth", depth, "--no-single-branch", repo_url, repo_path.to_s
41
+ else
42
+ git :clone, "--mirror", repo_url, repo_path.to_s
43
+ end
44
+ end
45
+
46
+ def update_mirror
47
+ # Note: Requires git version 1.9 or greater
48
+ if (depth = fetch(:git_shallow_clone))
49
+ git :fetch, "--depth", depth, "origin", fetch(:branch)
50
+ else
51
+ git :remote, :update, "--prune"
52
+ end
53
+ end
54
+
55
+ def archive_to_release_path
56
+ if (tree = fetch(:repo_tree))
57
+ tree = tree.slice %r#^/?(.*?)/?$#, 1
58
+ components = tree.split("/").size
59
+ git :archive, fetch(:branch), tree, "| #{SSHKit.config.command_map[:tar]} -x --strip-components #{components} -f - -C", release_path
60
+ else
61
+ git :archive, fetch(:branch), "| #{SSHKit.config.command_map[:tar]} -x -f - -C", release_path
62
+ end
63
+ end
64
+
65
+ def fetch_revision
66
+ backend.capture(:git, "rev-list --max-count=1 #{fetch(:branch)}")
67
+ end
68
+
69
+ def git(*args)
70
+ args.unshift :git
71
+ backend.execute(*args)
72
+ end
73
+ end
@@ -0,0 +1,48 @@
1
+ require "capistrano/scm/plugin"
2
+
3
+ class Capistrano::SCM::Hg < Capistrano::SCM::Plugin
4
+ def register_hooks
5
+ after "deploy:new_release_path", "hg:create_release"
6
+ before "deploy:check", "hg:check"
7
+ before "deploy:set_current_revision", "hg:set_current_revision"
8
+ end
9
+
10
+ def define_tasks
11
+ eval_rakefile File.expand_path("../tasks/hg.rake", __FILE__)
12
+ end
13
+
14
+ def hg(*args)
15
+ args.unshift(:hg)
16
+ backend.execute(*args)
17
+ end
18
+
19
+ def repo_mirror_exists?
20
+ backend.test " [ -d #{repo_path}/.hg ] "
21
+ end
22
+
23
+ def check_repo_is_reachable
24
+ hg "id", repo_url
25
+ end
26
+
27
+ def clone_repo
28
+ hg "clone", "--noupdate", repo_url, repo_path.to_s
29
+ end
30
+
31
+ def update_mirror
32
+ hg "pull"
33
+ end
34
+
35
+ def archive_to_release_path
36
+ if (tree = fetch(:repo_tree))
37
+ tree = tree.slice %r#^/?(.*?)/?$#, 1
38
+ components = tree.split("/").size
39
+ hg "archive --type tgz -p . -I", tree, "--rev", fetch(:branch), "| tar -x --strip-components #{components} -f - -C", release_path
40
+ else
41
+ hg "archive", release_path, "--rev", fetch(:branch)
42
+ end
43
+ end
44
+
45
+ def fetch_revision
46
+ backend.capture(:hg, "log --rev #{fetch(:branch)} --template \"{node}\n\"")
47
+ end
48
+ end
@@ -0,0 +1,13 @@
1
+ require "capistrano/plugin"
2
+ require "capistrano/scm"
3
+
4
+ # Base class for all built-in and third-party SCM plugins. Notice that this
5
+ # class doesn't really do anything other than provide an `scm?` predicate. This
6
+ # tells Capistrano that the plugin provides SCM functionality. All other plugin
7
+ # features are inherited from Capistrano::Plugin.
8
+ #
9
+ class Capistrano::SCM::Plugin < Capistrano::Plugin
10
+ def scm?
11
+ true
12
+ end
13
+ end
@@ -0,0 +1,47 @@
1
+ require "capistrano/scm/plugin"
2
+
3
+ class Capistrano::SCM::Svn < Capistrano::SCM::Plugin
4
+ def register_hooks
5
+ after "deploy:new_release_path", "svn:create_release"
6
+ before "deploy:check", "svn:check"
7
+ before "deploy:set_current_revision", "svn:set_current_revision"
8
+ end
9
+
10
+ def define_tasks
11
+ eval_rakefile File.expand_path("../tasks/svn.rake", __FILE__)
12
+ end
13
+
14
+ def svn(*args)
15
+ args.unshift(:svn)
16
+ args.push "--username #{fetch(:svn_username)}" if fetch(:svn_username)
17
+ args.push "--password #{fetch(:svn_password)}" if fetch(:svn_password)
18
+ args.push "--revision #{fetch(:svn_revision)}" if fetch(:svn_revision)
19
+ backend.execute(*args)
20
+ end
21
+
22
+ def repo_mirror_exists?
23
+ backend.test " [ -d #{repo_path}/.svn ] "
24
+ end
25
+
26
+ def check_repo_is_reachable
27
+ svn_username = fetch(:svn_username) ? "--username #{fetch(:svn_username)}" : ""
28
+ svn_password = fetch(:svn_password) ? "--password #{fetch(:svn_password)}" : ""
29
+ backend.test :svn, :info, repo_url, svn_username, svn_password
30
+ end
31
+
32
+ def clone_repo
33
+ svn :checkout, repo_url, repo_path.to_s
34
+ end
35
+
36
+ def update_mirror
37
+ svn :update
38
+ end
39
+
40
+ def archive_to_release_path
41
+ svn :export, "--force", ".", release_path
42
+ end
43
+
44
+ def fetch_revision
45
+ backend.capture(:svnversion, repo_path.to_s)
46
+ end
47
+ end
@@ -1,22 +1,7 @@
1
- namespace :git do
2
- def strategy
3
- @strategy ||= Capistrano::Git.new(self, fetch(:git_strategy, Capistrano::Git::DefaultStrategy))
4
- end
5
-
6
- set :git_wrapper_path, lambda {
7
- # Try to avoid permissions issues when multiple users deploy the same app
8
- # by using different file names in the same dir for each deployer and stage.
9
- suffix = [:application, :stage, :local_user].map { |key| fetch(key).to_s }.join("-").gsub(/\s+/, "-")
10
- "#{fetch(:tmp_dir)}/git-ssh-#{suffix}.sh"
11
- }
12
-
13
- set :git_environmental_variables, lambda {
14
- {
15
- git_askpass: "/bin/echo",
16
- git_ssh: fetch(:git_wrapper_path)
17
- }
18
- }
1
+ # This trick lets us access the Git plugin within `on` blocks.
2
+ git_plugin = self
19
3
 
4
+ namespace :git do
20
5
  desc "Upload the git wrapper script, this script guarantees that we can script git without getting an interactive prompt"
21
6
  task :wrapper do
22
7
  on release_roles :all do
@@ -31,7 +16,7 @@ namespace :git do
31
16
  fetch(:branch)
32
17
  on release_roles :all do
33
18
  with fetch(:git_environmental_variables) do
34
- strategy.check
19
+ git_plugin.check_repo_is_reachable
35
20
  end
36
21
  end
37
22
  end
@@ -39,12 +24,12 @@ namespace :git do
39
24
  desc "Clone the repo to the cache"
40
25
  task clone: :'git:wrapper' do
41
26
  on release_roles :all do
42
- if strategy.test
27
+ if git_plugin.repo_mirror_exists?
43
28
  info t(:mirror_exists, at: repo_path)
44
29
  else
45
30
  within deploy_path do
46
31
  with fetch(:git_environmental_variables) do
47
- strategy.clone
32
+ git_plugin.clone_repo
48
33
  end
49
34
  end
50
35
  end
@@ -56,7 +41,7 @@ namespace :git do
56
41
  on release_roles :all do
57
42
  within repo_path do
58
43
  with fetch(:git_environmental_variables) do
59
- strategy.update
44
+ git_plugin.update_mirror
60
45
  end
61
46
  end
62
47
  end
@@ -68,7 +53,7 @@ namespace :git do
68
53
  with fetch(:git_environmental_variables) do
69
54
  within repo_path do
70
55
  execute :mkdir, "-p", release_path
71
- strategy.release
56
+ git_plugin.archive_to_release_path
72
57
  end
73
58
  end
74
59
  end
@@ -79,7 +64,7 @@ namespace :git do
79
64
  on release_roles :all do
80
65
  within repo_path do
81
66
  with fetch(:git_environmental_variables) do
82
- set :current_revision, strategy.fetch_revision
67
+ set :current_revision, git_plugin.fetch_revision
83
68
  end
84
69
  end
85
70
  end
@@ -1,23 +1,24 @@
1
- namespace :hg do
2
- def strategy
3
- @strategy ||= Capistrano::Hg.new(self, fetch(:hg_strategy, Capistrano::Hg::DefaultStrategy))
4
- end
1
+ # TODO: this is nearly identical to git.rake. DRY up?
5
2
 
3
+ # This trick lets us access the Hg plugin within `on` blocks.
4
+ hg_plugin = self
5
+
6
+ namespace :hg do
6
7
  desc "Check that the repo is reachable"
7
8
  task :check do
8
9
  on release_roles :all do
9
- strategy.check
10
+ hg_plugin.check_repo_is_reachable
10
11
  end
11
12
  end
12
13
 
13
14
  desc "Clone the repo to the cache"
14
15
  task :clone do
15
16
  on release_roles :all do
16
- if strategy.test
17
+ if hg_plugin.repo_mirror_exists?
17
18
  info t(:mirror_exists, at: repo_path)
18
19
  else
19
20
  within deploy_path do
20
- strategy.clone
21
+ hg_plugin.clone_repo
21
22
  end
22
23
  end
23
24
  end
@@ -27,7 +28,7 @@ namespace :hg do
27
28
  task update: :'hg:clone' do
28
29
  on release_roles :all do
29
30
  within repo_path do
30
- strategy.update
31
+ hr.update_mirror
31
32
  end
32
33
  end
33
34
  end
@@ -36,7 +37,7 @@ namespace :hg do
36
37
  task create_release: :'hg:update' do
37
38
  on release_roles :all do
38
39
  within repo_path do
39
- strategy.release
40
+ hg_plugin.archive_to_release_path
40
41
  end
41
42
  end
42
43
  end
@@ -45,7 +46,7 @@ namespace :hg do
45
46
  task :set_current_revision do
46
47
  on release_roles :all do
47
48
  within repo_path do
48
- set :current_revision, strategy.fetch_revision
49
+ set :current_revision, hg_plugin.fetch_revision
49
50
  end
50
51
  end
51
52
  end
@@ -1,23 +1,24 @@
1
- namespace :svn do
2
- def strategy
3
- @strategy ||= Capistrano::Svn.new(self, fetch(:svn_strategy, Capistrano::Svn::DefaultStrategy))
4
- end
1
+ # TODO: this is nearly identical to git.rake. DRY up?
5
2
 
3
+ # This trick lets us access the Svn plugin within `on` blocks.
4
+ svn_plugin = self
5
+
6
+ namespace :svn do
6
7
  desc "Check that the repo is reachable"
7
8
  task :check do
8
9
  on release_roles :all do
9
- strategy.check
10
+ svn_plugin.check_repo_is_reachable
10
11
  end
11
12
  end
12
13
 
13
14
  desc "Clone the repo to the cache"
14
15
  task :clone do
15
16
  on release_roles :all do
16
- if strategy.test
17
+ if svn_plugin.repo_mirror_exists?
17
18
  info t(:mirror_exists, at: repo_path)
18
19
  else
19
20
  within deploy_path do
20
- strategy.clone
21
+ svn_plugin.clone_repo
21
22
  end
22
23
  end
23
24
  end
@@ -27,7 +28,7 @@ namespace :svn do
27
28
  task update: :'svn:clone' do
28
29
  on release_roles :all do
29
30
  within repo_path do
30
- strategy.update
31
+ svn_plugin.update_mirror
31
32
  end
32
33
  end
33
34
  end
@@ -36,7 +37,7 @@ namespace :svn do
36
37
  task create_release: :'svn:update' do
37
38
  on release_roles :all do
38
39
  within repo_path do
39
- strategy.release
40
+ svn_plugin.archive_to_release_path
40
41
  end
41
42
  end
42
43
  end
@@ -45,7 +46,7 @@ namespace :svn do
45
46
  task :set_current_revision do
46
47
  on release_roles :all do
47
48
  within repo_path do
48
- set :current_revision, strategy.fetch_revision
49
+ set :current_revision, svn_plugin.fetch_revision
49
50
  end
50
51
  end
51
52
  end