capistrano 3.4.0 → 3.17.1

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