capistrano 3.4.0 → 3.17.1

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 (138) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +129 -0
  3. data/.github/issue_template.md +19 -0
  4. data/.github/pull_request_template.md +22 -0
  5. data/.github/release-drafter.yml +17 -0
  6. data/.github/workflows/push.yml +12 -0
  7. data/.gitignore +8 -5
  8. data/.rubocop.yml +62 -0
  9. data/CHANGELOG.md +1 -307
  10. data/CONTRIBUTING.md +63 -93
  11. data/DEVELOPMENT.md +127 -0
  12. data/Dangerfile +1 -0
  13. data/Gemfile +40 -3
  14. data/LICENSE.txt +1 -1
  15. data/README.md +127 -44
  16. data/RELEASING.md +17 -0
  17. data/Rakefile +13 -2
  18. data/UPGRADING-3.7.md +86 -0
  19. data/bin/cap +1 -1
  20. data/capistrano.gemspec +21 -24
  21. data/features/deploy.feature +35 -1
  22. data/features/doctor.feature +11 -0
  23. data/features/installation.feature +8 -3
  24. data/features/stage_failure.feature +9 -0
  25. data/features/step_definitions/assertions.rb +51 -18
  26. data/features/step_definitions/cap_commands.rb +9 -0
  27. data/features/step_definitions/setup.rb +53 -9
  28. data/features/subdirectory.feature +9 -0
  29. data/features/support/env.rb +5 -5
  30. data/features/support/remote_command_helpers.rb +12 -6
  31. data/features/support/vagrant_helpers.rb +17 -11
  32. data/lib/Capfile +1 -1
  33. data/lib/capistrano/all.rb +10 -10
  34. data/lib/capistrano/application.rb +47 -34
  35. data/lib/capistrano/configuration/empty_filter.rb +9 -0
  36. data/lib/capistrano/configuration/filter.rb +17 -47
  37. data/lib/capistrano/configuration/host_filter.rb +29 -0
  38. data/lib/capistrano/configuration/null_filter.rb +9 -0
  39. data/lib/capistrano/configuration/plugin_installer.rb +51 -0
  40. data/lib/capistrano/configuration/question.rb +31 -9
  41. data/lib/capistrano/configuration/role_filter.rb +29 -0
  42. data/lib/capistrano/configuration/scm_resolver.rb +149 -0
  43. data/lib/capistrano/configuration/server.rb +29 -23
  44. data/lib/capistrano/configuration/servers.rb +21 -14
  45. data/lib/capistrano/configuration/validated_variables.rb +110 -0
  46. data/lib/capistrano/configuration/variables.rb +112 -0
  47. data/lib/capistrano/configuration.rb +91 -44
  48. data/lib/capistrano/defaults.rb +26 -4
  49. data/lib/capistrano/deploy.rb +1 -1
  50. data/lib/capistrano/doctor/environment_doctor.rb +19 -0
  51. data/lib/capistrano/doctor/gems_doctor.rb +45 -0
  52. data/lib/capistrano/doctor/output_helpers.rb +79 -0
  53. data/lib/capistrano/doctor/servers_doctor.rb +105 -0
  54. data/lib/capistrano/doctor/variables_doctor.rb +74 -0
  55. data/lib/capistrano/doctor.rb +6 -0
  56. data/lib/capistrano/dotfile.rb +1 -2
  57. data/lib/capistrano/dsl/env.rb +9 -47
  58. data/lib/capistrano/dsl/paths.rb +11 -25
  59. data/lib/capistrano/dsl/stages.rb +14 -2
  60. data/lib/capistrano/dsl/task_enhancements.rb +7 -12
  61. data/lib/capistrano/dsl.rb +47 -16
  62. data/lib/capistrano/framework.rb +1 -1
  63. data/lib/capistrano/i18n.rb +32 -24
  64. data/lib/capistrano/immutable_task.rb +30 -0
  65. data/lib/capistrano/install.rb +1 -1
  66. data/lib/capistrano/plugin.rb +95 -0
  67. data/lib/capistrano/proc_helpers.rb +13 -0
  68. data/lib/capistrano/scm/git.rb +100 -0
  69. data/lib/capistrano/scm/hg.rb +55 -0
  70. data/lib/capistrano/scm/plugin.rb +13 -0
  71. data/lib/capistrano/scm/svn.rb +56 -0
  72. data/lib/capistrano/scm/tasks/git.rake +73 -0
  73. data/lib/capistrano/scm/tasks/hg.rake +53 -0
  74. data/lib/capistrano/scm/tasks/svn.rake +53 -0
  75. data/lib/capistrano/scm.rb +7 -20
  76. data/lib/capistrano/setup.rb +20 -6
  77. data/lib/capistrano/tasks/console.rake +4 -8
  78. data/lib/capistrano/tasks/deploy.rake +105 -73
  79. data/lib/capistrano/tasks/doctor.rake +24 -0
  80. data/lib/capistrano/tasks/framework.rake +13 -14
  81. data/lib/capistrano/tasks/install.rake +14 -15
  82. data/lib/capistrano/templates/Capfile +21 -10
  83. data/lib/capistrano/templates/deploy.rb.erb +17 -26
  84. data/lib/capistrano/templates/stage.rb.erb +9 -9
  85. data/lib/capistrano/upload_task.rb +1 -1
  86. data/lib/capistrano/version.rb +1 -1
  87. data/lib/capistrano/version_validator.rb +5 -10
  88. data/spec/integration/dsl_spec.rb +289 -240
  89. data/spec/integration_spec_helper.rb +3 -5
  90. data/spec/lib/capistrano/application_spec.rb +23 -39
  91. data/spec/lib/capistrano/configuration/empty_filter_spec.rb +17 -0
  92. data/spec/lib/capistrano/configuration/filter_spec.rb +83 -85
  93. data/spec/lib/capistrano/configuration/host_filter_spec.rb +71 -0
  94. data/spec/lib/capistrano/configuration/null_filter_spec.rb +17 -0
  95. data/spec/lib/capistrano/configuration/plugin_installer_spec.rb +98 -0
  96. data/spec/lib/capistrano/configuration/question_spec.rb +58 -26
  97. data/spec/lib/capistrano/configuration/role_filter_spec.rb +80 -0
  98. data/spec/lib/capistrano/configuration/scm_resolver_spec.rb +55 -0
  99. data/spec/lib/capistrano/configuration/server_spec.rb +106 -113
  100. data/spec/lib/capistrano/configuration/servers_spec.rb +129 -145
  101. data/spec/lib/capistrano/configuration_spec.rb +224 -63
  102. data/spec/lib/capistrano/doctor/environment_doctor_spec.rb +44 -0
  103. data/spec/lib/capistrano/doctor/gems_doctor_spec.rb +67 -0
  104. data/spec/lib/capistrano/doctor/output_helpers_spec.rb +47 -0
  105. data/spec/lib/capistrano/doctor/servers_doctor_spec.rb +86 -0
  106. data/spec/lib/capistrano/doctor/variables_doctor_spec.rb +89 -0
  107. data/spec/lib/capistrano/dsl/paths_spec.rb +97 -59
  108. data/spec/lib/capistrano/dsl/task_enhancements_spec.rb +57 -37
  109. data/spec/lib/capistrano/dsl_spec.rb +84 -11
  110. data/spec/lib/capistrano/immutable_task_spec.rb +31 -0
  111. data/spec/lib/capistrano/plugin_spec.rb +84 -0
  112. data/spec/lib/capistrano/scm/git_spec.rb +184 -0
  113. data/spec/lib/capistrano/scm/hg_spec.rb +109 -0
  114. data/spec/lib/capistrano/scm/svn_spec.rb +137 -0
  115. data/spec/lib/capistrano/scm_spec.rb +7 -8
  116. data/spec/lib/capistrano/upload_task_spec.rb +7 -7
  117. data/spec/lib/capistrano/version_validator_spec.rb +61 -46
  118. data/spec/lib/capistrano_spec.rb +2 -3
  119. data/spec/spec_helper.rb +21 -8
  120. data/spec/support/Vagrantfile +9 -10
  121. data/spec/support/tasks/database.rake +3 -3
  122. data/spec/support/tasks/fail.rake +4 -3
  123. data/spec/support/tasks/failed.rake +2 -2
  124. data/spec/support/tasks/plugin.rake +6 -0
  125. data/spec/support/tasks/root.rake +4 -4
  126. data/spec/support/test_app.rb +64 -39
  127. metadata +100 -55
  128. data/.travis.yml +0 -13
  129. data/features/remote_file_task.feature +0 -14
  130. data/lib/capistrano/git.rb +0 -46
  131. data/lib/capistrano/hg.rb +0 -43
  132. data/lib/capistrano/svn.rb +0 -38
  133. data/lib/capistrano/tasks/git.rake +0 -81
  134. data/lib/capistrano/tasks/hg.rake +0 -52
  135. data/lib/capistrano/tasks/svn.rake +0 -52
  136. data/spec/lib/capistrano/git_spec.rb +0 -81
  137. data/spec/lib/capistrano/hg_spec.rb +0 -81
  138. data/spec/lib/capistrano/svn_spec.rb +0 -79
data/UPGRADING-3.7.md ADDED
@@ -0,0 +1,86 @@
1
+ # Capistrano 3.7.0 upgrade guide
2
+
3
+ ## The :scm variable is deprecated
4
+
5
+ Up until now, Capistrano's SCM was configured using the `:scm` variable:
6
+
7
+ ```ruby
8
+ # This is now deprecated
9
+ set :scm, :svn
10
+ ```
11
+
12
+ To avoid deprecation warnings:
13
+
14
+ 1. Remove `set :scm, ...` from your Capistrano configuration.
15
+ 2. Add *one* of the following SCM declarations to your `Capfile` after `require "capistrano/deploy"`:
16
+
17
+ ```ruby
18
+ # To use Git
19
+ require "capistrano/scm/git"
20
+ install_plugin Capistrano::SCM::Git
21
+
22
+ # To use Mercurial
23
+ require "capistrano/scm/hg"
24
+ install_plugin Capistrano::SCM::Hg
25
+
26
+ # To use Subversion
27
+ require "capistrano/scm/svn"
28
+ install_plugin Capistrano::SCM::Svn
29
+ ```
30
+
31
+ ## This is the last release where Git is the automatic default
32
+
33
+ If you do not specify an SCM, Capistrano assumes Git. However this behavior is
34
+ now deprecated. Add this to your Capfile to avoid deprecation warnings:
35
+
36
+ ```ruby
37
+ require "capistrano/scm/git"
38
+ install_plugin Capistrano::SCM::Git
39
+ ```
40
+
41
+ ## :git_strategy, :hg_strategy, and :svn_strategy are removed
42
+
43
+ Capistrano 3.7.0 has a rewritten SCM system that relies on "plugins". This
44
+ system is more flexible than the old "strategy" system that only allowed certain
45
+ parts of SCM tasks to be customized.
46
+
47
+ If your deployment relies on a custom SCM strategy, you will need to rewrite
48
+ that strategy to be a full-fledged SCM plugin instead. There is a fairly
49
+ straightforward migration path: write your plugin to be a subclass of the
50
+ built-in SCM that you want to customize. For example:
51
+
52
+ ```ruby
53
+ require "capistrano/scm/git"
54
+
55
+ class MyCustomGit < Capistrano::SCM::Git
56
+ # Override the methods you wish to customize, e.g.:
57
+ def clone_repo
58
+ # ...
59
+ end
60
+ end
61
+ ```
62
+
63
+ Then use your plugin in by loading it in the Capfile:
64
+
65
+ ```ruby
66
+ require_relative "path/to/my_custom_git.rb"
67
+ install_plugin MyCustomGit
68
+ ```
69
+
70
+ ## Existing third-party SCMs are deprecated
71
+
72
+ If you are using a third-party SCM, you can continue using it without
73
+ changes, but you will see deprecation warnings. Contact the maintainer of the
74
+ third-party SCM gem and ask them about modifying the gem to work with the new
75
+ Capistrano 3.7.0 SCM plugin system.
76
+
77
+ ## remote_file is removed
78
+
79
+ The `remote_file` method is no longer in Capistrano 3.7.0. You can read the
80
+ discussion that led to its removal here:
81
+ [issue 762](https://github.com/capistrano/capistrano/issues/762).
82
+
83
+ There is no direct replacement. To migrate to 3.7.0, you will need to rewrite
84
+ any parts of your deployment that use `remote_file` to use a different
85
+ mechanism for uploading files. Consider using the `upload!` method directly in
86
+ a procedural fashion instead.
data/bin/cap CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env ruby
2
- require 'capistrano/all'
2
+ require "capistrano/all"
3
3
  Capistrano::Application.new.run
data/capistrano.gemspec CHANGED
@@ -1,37 +1,34 @@
1
1
  # -*- encoding: utf-8 -*-
2
- lib = File.expand_path('../lib', __FILE__)
2
+
3
+ lib = File.expand_path("../lib", __FILE__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'capistrano/version'
5
+ require "capistrano/version"
5
6
 
6
7
  Gem::Specification.new do |gem|
7
8
  gem.name = "capistrano"
8
9
  gem.version = Capistrano::VERSION
9
10
  gem.authors = ["Tom Clements", "Lee Hambley"]
10
11
  gem.email = ["seenmyfate@gmail.com", "lee.hambley@gmail.com"]
11
- gem.description = %q{Capistrano is a utility and framework for executing commands in parallel on multiple remote machines, via SSH.}
12
- gem.summary = %q{Capistrano - Welcome to easy deployment with Ruby over SSH}
13
- gem.homepage = "http://capistranorb.com/"
14
-
15
- gem.files = `git ls-files`.split($/)
16
- gem.executables = ['cap', 'capify']
12
+ gem.description = "Capistrano is a utility and framework for executing commands in parallel on multiple remote machines, via SSH."
13
+ gem.summary = "Capistrano - Welcome to easy deployment with Ruby over SSH"
14
+ gem.homepage = "https://capistranorb.com/"
15
+ gem.metadata = {
16
+ "bug_tracker_uri" => "https://github.com/capistrano/capistrano/issues",
17
+ "changelog_uri" => "https://github.com/capistrano/capistrano/releases",
18
+ "source_code_uri" => "https://github.com/capistrano/capistrano",
19
+ "homepage_uri" => "https://capistranorb.com/",
20
+ "documentation_uri" => "https://capistranorb.com/"
21
+ }
22
+ gem.files = `git ls-files -z`.split("\x0").reject { |f| f =~ /^docs/ }
23
+ gem.executables = %w(cap capify)
17
24
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
25
  gem.require_paths = ["lib"]
19
26
 
20
- gem.licenses = ['MIT']
21
-
22
- gem.post_install_message = <<eos
23
- Capistrano 3.1 has some breaking changes. Please check the CHANGELOG: http://goo.gl/SxB0lr
24
-
25
- If you're upgrading Capistrano from 2.x, we recommend to read the upgrade guide: http://goo.gl/4536kB
26
-
27
- The `deploy:restart` hook for passenger applications is now in a separate gem called capistrano-passenger. Just add it to your Gemfile and require it in your Capfile.
28
- eos
29
-
30
- gem.required_ruby_version = '>= 1.9.3'
31
- gem.add_dependency 'sshkit', '~> 1.3'
32
- gem.add_dependency 'rake', '>= 10.0.0'
33
- gem.add_dependency 'i18n'
27
+ gem.licenses = ["MIT"]
34
28
 
35
- gem.add_development_dependency 'rspec'
36
- gem.add_development_dependency 'mocha'
29
+ gem.required_ruby_version = ">= 2.0"
30
+ gem.add_dependency "airbrussh", ">= 1.0.0"
31
+ gem.add_dependency "i18n"
32
+ gem.add_dependency "rake", ">= 10.0.0"
33
+ gem.add_dependency "sshkit", ">= 1.9.0"
37
34
  end
@@ -7,7 +7,7 @@ Feature: Deploy
7
7
  Scenario: Creating the repo
8
8
  When I run cap "git:check"
9
9
  Then the task is successful
10
- And references in the remote repo are listed
10
+ And git wrapper permissions are 0700
11
11
 
12
12
  Scenario: Creating the directory structure
13
13
  When I run cap "deploy:check:directories"
@@ -52,3 +52,37 @@ Feature: Deploy
52
52
  When I run cap "deploy:symlink:release"
53
53
  Then the current directory will be a symlink to the release
54
54
 
55
+ Scenario: Cleanup
56
+ Given config stage file has line "set :keep_releases, 3"
57
+ And 5 valid existing releases
58
+ And an invalid release named "new"
59
+ When I run cap "deploy:cleanup"
60
+ Then 3 valid releases are kept
61
+ And the invalid "new" release is ignored
62
+
63
+ Scenario: Cleanup after many failed releases doesn't remove last good release
64
+ Given config stage file has line "set :keep_releases, 2"
65
+ And I make 2 deployments
66
+ And an invalid release named "77777777777777"
67
+ And an invalid release named "88888888888888"
68
+ And an invalid release named "99999999999999"
69
+ When I run cap "deploy:cleanup"
70
+ Then 3 valid releases are kept
71
+ And the current directory will be a symlink to the release
72
+
73
+ Scenario: Cleanup when there are more releases than arguments can handle
74
+ Given config stage file has line "set :keep_releases, 3"
75
+ And 5000 valid existing releases
76
+ When I run cap "deploy:cleanup"
77
+ Then 3 valid releases are kept
78
+
79
+ Scenario: Rolling Back
80
+ Given I make 2 deployments
81
+ When I run cap "deploy:rollback"
82
+ Then the current symlink points to the previous release
83
+
84
+ Scenario: Rolling Back to a specific release
85
+ Given I make 3 deployments
86
+ When I rollback to a specific release
87
+ Then the current symlink points to that specific release
88
+
@@ -0,0 +1,11 @@
1
+ Feature: Doctor
2
+
3
+ Background:
4
+ Given a test app with the default configuration
5
+
6
+ Scenario: Running the doctor task
7
+ When I run cap "doctor"
8
+ Then the task is successful
9
+ And contains "Environment" in the output
10
+ And contains "Gems" in the output
11
+ And contains "Variables" in the output
@@ -1,16 +1,21 @@
1
1
  Feature: Installation
2
2
 
3
3
  Background:
4
- Given a test app with the default configuration
4
+ Given a test app without any configuration
5
+
6
+ Scenario: The "install" task documentation can be viewed
7
+ When I run "cap -T"
8
+ Then the task is successful
9
+ And contains "cap install" in the output
5
10
 
6
11
  Scenario: With default stages
7
- When I run cap "install"
12
+ When I run "cap install"
8
13
  Then the deploy.rb file is created
9
14
  And the default stage files are created
10
15
  And the tasks folder is created
11
16
 
12
17
  Scenario: With specified stages
13
- When I run cap "install STAGES=qa,production"
18
+ When I run "cap install STAGES=qa,production"
14
19
  Then the deploy.rb file is created
15
20
  And the specified stage files are created
16
21
  And the tasks folder is created
@@ -0,0 +1,9 @@
1
+ Feature: Stage failure
2
+
3
+ Background:
4
+ Given a test app with the default configuration
5
+ And a stage file named deploy.rb
6
+
7
+ Scenario: Running a task
8
+ When I run cap "doctor"
9
+ Then the task fails
@@ -1,5 +1,14 @@
1
+ require "shellwords"
2
+
1
3
  Then(/^references in the remote repo are listed$/) do
2
- expect(@output).to include('refs/heads/master')
4
+ expect(@output).to include("refs/heads/master")
5
+ end
6
+
7
+ Then(/^git wrapper permissions are 0700$/) do
8
+ permissions_test = %Q([ $(stat -c "%a" #{TestApp.git_wrapper_path_glob}) == "700" ])
9
+ _stdout, _stderr, status = vagrant_cli_command("ssh -c #{permissions_test.shellescape}")
10
+
11
+ expect(status).to be_success
3
12
  end
4
13
 
5
14
  Then(/^the shared path is created$/) do
@@ -10,6 +19,18 @@ Then(/^the releases path is created$/) do
10
19
  run_vagrant_command(test_dir_exists(TestApp.releases_path))
11
20
  end
12
21
 
22
+ Then(/^(\d+) valid releases are kept/) do |num|
23
+ test = %Q([ $(ls -g #{TestApp.releases_path} | grep -E '[0-9]{14}' | wc -l) == "#{num}" ])
24
+ _, _, status = vagrant_cli_command("ssh -c #{test.shellescape}")
25
+ expect(status).to be_success
26
+ end
27
+
28
+ Then(/^the invalid (.+) release is ignored$/) do |filename|
29
+ test = "ls -g #{TestApp.releases_path} | grep #{filename}"
30
+ _, _, status = vagrant_cli_command("ssh -c #{test.shellescape}")
31
+ expect(status).to be_success
32
+ end
33
+
13
34
  Then(/^directories in :linked_dirs are created in shared$/) do
14
35
  TestApp.linked_dirs.each do |dir|
15
36
  run_vagrant_command(test_dir_exists(TestApp.shared_path.join(dir)))
@@ -18,7 +39,7 @@ end
18
39
 
19
40
  Then(/^directories referenced in :linked_files are created in shared$/) do
20
41
  dirs = TestApp.linked_files.map { |path| TestApp.shared_path.join(path).dirname }
21
- dirs.each do | dir|
42
+ dirs.each do |dir|
22
43
  run_vagrant_command(test_dir_exists(dir))
23
44
  end
24
45
  end
@@ -45,31 +66,31 @@ Then(/^directory symlinks are created in the new release$/) do
45
66
  end
46
67
 
47
68
  Then(/^the current directory will be a symlink to the release$/) do
48
- run_vagrant_command(test_symlink_exists(TestApp.current_path))
69
+ run_vagrant_command(exists?("e", TestApp.current_path))
49
70
  end
50
71
 
51
72
  Then(/^the deploy\.rb file is created$/) do
52
- file = TestApp.test_app_path.join('config/deploy.rb')
53
- expect(File.exists?(file)).to be true
73
+ file = TestApp.test_app_path.join("config/deploy.rb")
74
+ expect(File.exist?(file)).to be true
54
75
  end
55
76
 
56
77
  Then(/^the default stage files are created$/) do
57
- staging = TestApp.test_app_path.join('config/deploy/staging.rb')
58
- production = TestApp.test_app_path.join('config/deploy/production.rb')
59
- expect(File.exists?(staging)).to be true
60
- expect(File.exists?(production)).to be true
78
+ staging = TestApp.test_app_path.join("config/deploy/staging.rb")
79
+ production = TestApp.test_app_path.join("config/deploy/production.rb")
80
+ expect(File.exist?(staging)).to be true
81
+ expect(File.exist?(production)).to be true
61
82
  end
62
83
 
63
84
  Then(/^the tasks folder is created$/) do
64
- path = TestApp.test_app_path.join('lib/capistrano/tasks')
65
- expect(Dir.exists?(path)).to be true
85
+ path = TestApp.test_app_path.join("lib/capistrano/tasks")
86
+ expect(Dir.exist?(path)).to be true
66
87
  end
67
88
 
68
89
  Then(/^the specified stage files are created$/) do
69
- qa = TestApp.test_app_path.join('config/deploy/qa.rb')
70
- production = TestApp.test_app_path.join('config/deploy/production.rb')
71
- expect(File.exists?(qa)).to be true
72
- expect(File.exists?(production)).to be true
90
+ qa = TestApp.test_app_path.join("config/deploy/qa.rb")
91
+ production = TestApp.test_app_path.join("config/deploy/production.rb")
92
+ expect(File.exist?(qa)).to be true
93
+ expect(File.exist?(production)).to be true
73
94
  end
74
95
 
75
96
  Then(/^it creates the file with the remote_task prerequisite$/) do
@@ -91,18 +112,18 @@ Then(/^the task fails$/) do
91
112
  end
92
113
 
93
114
  Then(/^the failure task will run$/) do
94
- failed = TestApp.shared_path.join('failed')
115
+ failed = TestApp.shared_path.join("failed")
95
116
  run_vagrant_command(test_file_exists(failed))
96
117
  end
97
118
 
98
119
  Then(/^the failure task will not run$/) do
99
- failed = TestApp.shared_path.join('failed')
120
+ failed = TestApp.shared_path.join("failed")
100
121
  expect { run_vagrant_command(test_file_exists(failed)) }
101
122
  .to raise_error(VagrantHelpers::VagrantSSHCommandError)
102
123
  end
103
124
 
104
125
  When(/^an error is raised$/) do
105
- error = TestApp.shared_path.join('fail')
126
+ error = TestApp.shared_path.join("fail")
106
127
  run_vagrant_command(test_file_exists(error))
107
128
  end
108
129
 
@@ -117,3 +138,15 @@ end
117
138
  Then(/doesn't contain "([^"]*)" in the output/) do |expected|
118
139
  expect(@output).not_to include(expected)
119
140
  end
141
+
142
+ Then(/the current symlink points to the previous release/) do
143
+ previous_release_path = @release_paths[-2]
144
+
145
+ run_vagrant_command(symlinked?(TestApp.current_path, previous_release_path))
146
+ end
147
+
148
+ Then(/^the current symlink points to that specific release$/) do
149
+ specific_release_path = TestApp.releases_path.join(@rollback_release)
150
+
151
+ run_vagrant_command(symlinked?(TestApp.current_path, specific_release_path))
152
+ end
@@ -2,6 +2,10 @@ When(/^I run cap "(.*?)"$/) do |task|
2
2
  @success, @output = TestApp.cap(task)
3
3
  end
4
4
 
5
+ When(/^I run cap "(.*?)" within the "(.*?)" directory$/) do |task, directory|
6
+ @success, @output = TestApp.cap(task, directory)
7
+ end
8
+
5
9
  When(/^I run cap "(.*?)" as part of a release$/) do |task|
6
10
  TestApp.cap("deploy:new_release_path #{task}")
7
11
  end
@@ -10,3 +14,8 @@ When(/^I run "(.*?)"$/) do |command|
10
14
  @success, @output = TestApp.run(command)
11
15
  end
12
16
 
17
+ When(/^I rollback to a specific release$/) do
18
+ @rollback_release = @release_paths.first.split("/").last
19
+
20
+ step %Q{I run cap "deploy:rollback ROLLBACK_RELEASE=#{@rollback_release}"}
21
+ end
@@ -2,8 +2,16 @@ Given(/^a test app with the default configuration$/) do
2
2
  TestApp.install
3
3
  end
4
4
 
5
+ Given(/^a test app without any configuration$/) do
6
+ TestApp.create_test_app
7
+ end
8
+
5
9
  Given(/^servers with the roles app and web$/) do
6
- vagrant_cli_command('up') rescue nil
10
+ begin
11
+ vagrant_cli_command("up")
12
+ rescue
13
+ nil
14
+ end
7
15
  end
8
16
 
9
17
  Given(/^a linked file "(.*?)"$/) do |file|
@@ -13,10 +21,16 @@ end
13
21
 
14
22
  Given(/^file "(.*?)" exists in shared path$/) do |file|
15
23
  file_shared_path = TestApp.shared_path.join(file)
16
- run_vagrant_command("mkdir -p #{TestApp.shared_path}")
24
+ run_vagrant_command("mkdir -p #{file_shared_path.dirname}")
17
25
  run_vagrant_command("touch #{file_shared_path}")
18
26
  end
19
27
 
28
+ Given(/^all linked files exists in shared path$/) do
29
+ TestApp.linked_files.each do |linked_file|
30
+ step %Q{file "#{linked_file}" exists in shared path}
31
+ end
32
+ end
33
+
20
34
  Given(/^file "(.*?)" does not exist in shared path$/) do |file|
21
35
  file_shared_path = TestApp.shared_path.join(file)
22
36
  run_vagrant_command("mkdir -p #{TestApp.shared_path}")
@@ -24,11 +38,11 @@ Given(/^file "(.*?)" does not exist in shared path$/) do |file|
24
38
  end
25
39
 
26
40
  Given(/^a custom task to generate a file$/) do
27
- TestApp.copy_task_to_test_app('spec/support/tasks/database.rake')
41
+ TestApp.copy_task_to_test_app("spec/support/tasks/database.rake")
28
42
  end
29
43
 
30
44
  Given(/^a task which executes as root$/) do
31
- TestApp.copy_task_to_test_app('spec/support/tasks/root.rake')
45
+ TestApp.copy_task_to_test_app("spec/support/tasks/root.rake")
32
46
  end
33
47
 
34
48
  Given(/config stage file has line "(.*?)"/) do |line|
@@ -36,15 +50,45 @@ Given(/config stage file has line "(.*?)"/) do |line|
36
50
  end
37
51
 
38
52
  Given(/^the configuration is in a custom location$/) do
39
- TestApp.move_configuration_to_custom_location('app')
53
+ TestApp.move_configuration_to_custom_location("app")
40
54
  end
41
55
 
42
56
  Given(/^a custom task that will simulate a failure$/) do
43
- safely_remove_file(TestApp.shared_path.join('failed'))
44
- TestApp.copy_task_to_test_app('spec/support/tasks/fail.rake')
57
+ safely_remove_file(TestApp.shared_path.join("failed"))
58
+ TestApp.copy_task_to_test_app("spec/support/tasks/fail.rake")
45
59
  end
46
60
 
47
61
  Given(/^a custom task to run in the event of a failure$/) do
48
- safely_remove_file(TestApp.shared_path.join('failed'))
49
- TestApp.copy_task_to_test_app('spec/support/tasks/failed.rake')
62
+ safely_remove_file(TestApp.shared_path.join("failed"))
63
+ TestApp.copy_task_to_test_app("spec/support/tasks/failed.rake")
64
+ end
65
+
66
+ Given(/^a stage file named (.+)$/) do |filename|
67
+ TestApp.write_local_stage_file(filename)
68
+ end
69
+
70
+ Given(/^I make (\d+) deployments$/) do |count|
71
+ step "all linked files exists in shared path"
72
+
73
+ @release_paths = (1..count.to_i).map do
74
+ TestApp.cap("deploy")
75
+ stdout, _stderr = run_vagrant_command("readlink #{TestApp.current_path}")
76
+
77
+ stdout.strip
78
+ end
79
+ end
80
+
81
+ Given(/^(\d+) valid existing releases$/) do |num|
82
+ a_day = 86_400 # in seconds
83
+ (1...num).each_slice(100) do |num_batch|
84
+ dirs = num_batch.map do |i|
85
+ offset = -(a_day * i)
86
+ TestApp.release_path(TestApp.timestamp(offset))
87
+ end
88
+ run_vagrant_command("mkdir -p #{dirs.join(' ')}")
89
+ end
90
+ end
91
+
92
+ Given(/^an invalid release named "(.+)"$/) do |filename|
93
+ run_vagrant_command("mkdir -p #{TestApp.release_path(filename)}")
50
94
  end
@@ -0,0 +1,9 @@
1
+ Feature: cap can be run from a subdirectory, and will still find the Capfile
2
+
3
+ Background:
4
+ Given a test app with the default configuration
5
+ And servers with the roles app and web
6
+
7
+ Scenario: Running cap from a subdirectory
8
+ When I run cap "git:check" within the "config" directory
9
+ Then the task is successful
@@ -1,11 +1,11 @@
1
- PROJECT_ROOT = File.expand_path('../../../', __FILE__)
2
- VAGRANT_ROOT = File.join(PROJECT_ROOT, 'spec/support')
3
- VAGRANT_BIN = ENV['VAGRANT_BIN'] || "vagrant"
1
+ PROJECT_ROOT = File.expand_path("../../../", __FILE__)
2
+ VAGRANT_ROOT = File.join(PROJECT_ROOT, "spec/support")
3
+ VAGRANT_BIN = ENV["VAGRANT_BIN"] || "vagrant"
4
4
 
5
5
  at_exit do
6
- if ENV['KEEP_RUNNING']
6
+ if ENV["KEEP_RUNNING"]
7
7
  VagrantHelpers.run_vagrant_command("rm -rf /home/vagrant/var")
8
8
  end
9
9
  end
10
10
 
11
- require_relative '../../spec/support/test_app'
11
+ require_relative "../../spec/support/test_app"
@@ -1,22 +1,28 @@
1
1
  module RemoteCommandHelpers
2
2
  def test_dir_exists(path)
3
- exists?('d', path)
3
+ exists?("d", path)
4
4
  end
5
5
 
6
6
  def test_symlink_exists(path)
7
- exists?('L', path)
7
+ exists?("L", path)
8
8
  end
9
9
 
10
10
  def test_file_exists(path)
11
- exists?('f', path)
11
+ exists?("f", path)
12
12
  end
13
13
 
14
14
  def exists?(type, path)
15
- %{[ -#{type} "#{path}" ]}
15
+ %Q{[ -#{type} "#{path}" ]}
16
16
  end
17
17
 
18
- def safely_remove_file(path)
19
- run_vagrant_command("rm #{test_file}") rescue VagrantHelpers::VagrantSSHCommandError
18
+ def symlinked?(symlink_path, target_path)
19
+ "[ #{symlink_path} -ef #{target_path} ]"
20
+ end
21
+
22
+ def safely_remove_file(_path)
23
+ run_vagrant_command("rm #{test_file}")
24
+ rescue
25
+ VagrantHelpers::VagrantSSHCommandError
20
26
  end
21
27
  end
22
28
 
@@ -1,10 +1,12 @@
1
+ require "open3"
2
+
1
3
  module VagrantHelpers
2
4
  extend self
3
5
 
4
6
  class VagrantSSHCommandError < RuntimeError; end
5
7
 
6
8
  at_exit do
7
- if ENV['KEEP_RUNNING']
9
+ if ENV["KEEP_RUNNING"]
8
10
  puts "Vagrant vm will be left up because KEEP_RUNNING is set."
9
11
  puts "Rerun without KEEP_RUNNING set to cleanup the vm."
10
12
  else
@@ -14,22 +16,26 @@ module VagrantHelpers
14
16
 
15
17
  def vagrant_cli_command(command)
16
18
  puts "[vagrant] #{command}"
17
- Dir.chdir(VAGRANT_ROOT) do
18
- `#{VAGRANT_BIN} #{command} 2>&1`.split("\n").each do |line|
19
- puts "[vagrant] #{line}"
20
- end
19
+ stdout, stderr, status = Dir.chdir(VAGRANT_ROOT) do
20
+ Open3.capture3("#{VAGRANT_BIN} #{command}")
21
21
  end
22
- $?
22
+
23
+ (stdout + stderr).each_line { |line| puts "[vagrant] #{line}" }
24
+
25
+ [stdout, stderr, status]
23
26
  end
24
27
 
25
28
  def run_vagrant_command(command)
26
- if (status = vagrant_cli_command("ssh -c #{command.inspect}")).success?
27
- true
28
- else
29
- fail VagrantSSHCommandError, status
30
- end
29
+ stdout, stderr, status = vagrant_cli_command("ssh -c #{command.inspect}")
30
+ return [stdout, stderr] if status.success?
31
+ raise VagrantSSHCommandError, status
31
32
  end
32
33
 
34
+ def puts(message)
35
+ # Attach log messages to the current cucumber feature (`log`),
36
+ # or simply puts to the console (`super`) if we are outside of cucumber.
37
+ respond_to?(:log) ? log(message) : super(message)
38
+ end
33
39
  end
34
40
 
35
41
  World(VagrantHelpers)
data/lib/Capfile CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env cap
2
2
  include Capistrano::DSL
3
- require 'capistrano/install'
3
+ require "capistrano/install"
@@ -1,17 +1,17 @@
1
- require 'rake'
2
- require 'sshkit'
1
+ require "rake"
2
+ require "sshkit"
3
3
 
4
- require 'io/console'
4
+ require "io/console"
5
5
 
6
6
  Rake.application.options.trace = true
7
7
 
8
- require 'capistrano/version'
9
- require 'capistrano/version_validator'
10
- require 'capistrano/i18n'
11
- require 'capistrano/dsl'
12
- require 'capistrano/application'
13
- require 'capistrano/configuration'
8
+ require "capistrano/version"
9
+ require "capistrano/version_validator"
10
+ require "capistrano/i18n"
11
+ require "capistrano/dsl"
12
+ require "capistrano/application"
13
+ require "capistrano/configuration"
14
+ require "capistrano/configuration/scm_resolver"
14
15
 
15
16
  module Capistrano
16
-
17
17
  end