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.
- checksums.yaml +5 -5
- data/.circleci/config.yml +129 -0
- data/.github/issue_template.md +19 -0
- data/.github/pull_request_template.md +22 -0
- data/.github/release-drafter.yml +17 -0
- data/.github/workflows/push.yml +12 -0
- data/.gitignore +8 -5
- data/.rubocop.yml +62 -0
- data/CHANGELOG.md +1 -307
- data/CONTRIBUTING.md +63 -93
- data/DEVELOPMENT.md +127 -0
- data/Dangerfile +1 -0
- data/Gemfile +40 -3
- data/LICENSE.txt +1 -1
- data/README.md +127 -44
- data/RELEASING.md +17 -0
- data/Rakefile +13 -2
- data/UPGRADING-3.7.md +86 -0
- data/bin/cap +1 -1
- data/capistrano.gemspec +21 -24
- data/features/deploy.feature +35 -1
- data/features/doctor.feature +11 -0
- data/features/installation.feature +8 -3
- data/features/stage_failure.feature +9 -0
- data/features/step_definitions/assertions.rb +51 -18
- data/features/step_definitions/cap_commands.rb +9 -0
- data/features/step_definitions/setup.rb +53 -9
- data/features/subdirectory.feature +9 -0
- data/features/support/env.rb +5 -5
- data/features/support/remote_command_helpers.rb +12 -6
- data/features/support/vagrant_helpers.rb +17 -11
- data/lib/Capfile +1 -1
- data/lib/capistrano/all.rb +10 -10
- data/lib/capistrano/application.rb +47 -34
- data/lib/capistrano/configuration/empty_filter.rb +9 -0
- data/lib/capistrano/configuration/filter.rb +17 -47
- data/lib/capistrano/configuration/host_filter.rb +29 -0
- data/lib/capistrano/configuration/null_filter.rb +9 -0
- data/lib/capistrano/configuration/plugin_installer.rb +51 -0
- data/lib/capistrano/configuration/question.rb +31 -9
- data/lib/capistrano/configuration/role_filter.rb +29 -0
- data/lib/capistrano/configuration/scm_resolver.rb +149 -0
- data/lib/capistrano/configuration/server.rb +29 -23
- data/lib/capistrano/configuration/servers.rb +21 -14
- data/lib/capistrano/configuration/validated_variables.rb +110 -0
- data/lib/capistrano/configuration/variables.rb +112 -0
- data/lib/capistrano/configuration.rb +91 -44
- data/lib/capistrano/defaults.rb +26 -4
- data/lib/capistrano/deploy.rb +1 -1
- data/lib/capistrano/doctor/environment_doctor.rb +19 -0
- data/lib/capistrano/doctor/gems_doctor.rb +45 -0
- data/lib/capistrano/doctor/output_helpers.rb +79 -0
- data/lib/capistrano/doctor/servers_doctor.rb +105 -0
- data/lib/capistrano/doctor/variables_doctor.rb +74 -0
- data/lib/capistrano/doctor.rb +6 -0
- data/lib/capistrano/dotfile.rb +1 -2
- data/lib/capistrano/dsl/env.rb +9 -47
- data/lib/capistrano/dsl/paths.rb +11 -25
- data/lib/capistrano/dsl/stages.rb +14 -2
- data/lib/capistrano/dsl/task_enhancements.rb +7 -12
- data/lib/capistrano/dsl.rb +47 -16
- data/lib/capistrano/framework.rb +1 -1
- data/lib/capistrano/i18n.rb +32 -24
- data/lib/capistrano/immutable_task.rb +30 -0
- data/lib/capistrano/install.rb +1 -1
- data/lib/capistrano/plugin.rb +95 -0
- data/lib/capistrano/proc_helpers.rb +13 -0
- data/lib/capistrano/scm/git.rb +100 -0
- data/lib/capistrano/scm/hg.rb +55 -0
- data/lib/capistrano/scm/plugin.rb +13 -0
- data/lib/capistrano/scm/svn.rb +56 -0
- data/lib/capistrano/scm/tasks/git.rake +73 -0
- data/lib/capistrano/scm/tasks/hg.rake +53 -0
- data/lib/capistrano/scm/tasks/svn.rake +53 -0
- data/lib/capistrano/scm.rb +7 -20
- data/lib/capistrano/setup.rb +20 -6
- data/lib/capistrano/tasks/console.rake +4 -8
- data/lib/capistrano/tasks/deploy.rake +105 -73
- data/lib/capistrano/tasks/doctor.rake +24 -0
- data/lib/capistrano/tasks/framework.rake +13 -14
- data/lib/capistrano/tasks/install.rake +14 -15
- data/lib/capistrano/templates/Capfile +21 -10
- data/lib/capistrano/templates/deploy.rb.erb +17 -26
- data/lib/capistrano/templates/stage.rb.erb +9 -9
- data/lib/capistrano/upload_task.rb +1 -1
- data/lib/capistrano/version.rb +1 -1
- data/lib/capistrano/version_validator.rb +5 -10
- data/spec/integration/dsl_spec.rb +289 -240
- data/spec/integration_spec_helper.rb +3 -5
- data/spec/lib/capistrano/application_spec.rb +23 -39
- data/spec/lib/capistrano/configuration/empty_filter_spec.rb +17 -0
- data/spec/lib/capistrano/configuration/filter_spec.rb +83 -85
- data/spec/lib/capistrano/configuration/host_filter_spec.rb +71 -0
- data/spec/lib/capistrano/configuration/null_filter_spec.rb +17 -0
- data/spec/lib/capistrano/configuration/plugin_installer_spec.rb +98 -0
- data/spec/lib/capistrano/configuration/question_spec.rb +58 -26
- data/spec/lib/capistrano/configuration/role_filter_spec.rb +80 -0
- data/spec/lib/capistrano/configuration/scm_resolver_spec.rb +55 -0
- data/spec/lib/capistrano/configuration/server_spec.rb +106 -113
- data/spec/lib/capistrano/configuration/servers_spec.rb +129 -145
- data/spec/lib/capistrano/configuration_spec.rb +224 -63
- data/spec/lib/capistrano/doctor/environment_doctor_spec.rb +44 -0
- data/spec/lib/capistrano/doctor/gems_doctor_spec.rb +67 -0
- data/spec/lib/capistrano/doctor/output_helpers_spec.rb +47 -0
- data/spec/lib/capistrano/doctor/servers_doctor_spec.rb +86 -0
- data/spec/lib/capistrano/doctor/variables_doctor_spec.rb +89 -0
- data/spec/lib/capistrano/dsl/paths_spec.rb +97 -59
- data/spec/lib/capistrano/dsl/task_enhancements_spec.rb +57 -37
- data/spec/lib/capistrano/dsl_spec.rb +84 -11
- data/spec/lib/capistrano/immutable_task_spec.rb +31 -0
- data/spec/lib/capistrano/plugin_spec.rb +84 -0
- data/spec/lib/capistrano/scm/git_spec.rb +184 -0
- data/spec/lib/capistrano/scm/hg_spec.rb +109 -0
- data/spec/lib/capistrano/scm/svn_spec.rb +137 -0
- data/spec/lib/capistrano/scm_spec.rb +7 -8
- data/spec/lib/capistrano/upload_task_spec.rb +7 -7
- data/spec/lib/capistrano/version_validator_spec.rb +61 -46
- data/spec/lib/capistrano_spec.rb +2 -3
- data/spec/spec_helper.rb +21 -8
- data/spec/support/Vagrantfile +9 -10
- data/spec/support/tasks/database.rake +3 -3
- data/spec/support/tasks/fail.rake +4 -3
- data/spec/support/tasks/failed.rake +2 -2
- data/spec/support/tasks/plugin.rake +6 -0
- data/spec/support/tasks/root.rake +4 -4
- data/spec/support/test_app.rb +64 -39
- metadata +100 -55
- data/.travis.yml +0 -13
- data/features/remote_file_task.feature +0 -14
- data/lib/capistrano/git.rb +0 -46
- data/lib/capistrano/hg.rb +0 -43
- data/lib/capistrano/svn.rb +0 -38
- data/lib/capistrano/tasks/git.rake +0 -81
- data/lib/capistrano/tasks/hg.rake +0 -52
- data/lib/capistrano/tasks/svn.rake +0 -52
- data/spec/lib/capistrano/git_spec.rb +0 -81
- data/spec/lib/capistrano/hg_spec.rb +0 -81
- 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
data/capistrano.gemspec
CHANGED
|
@@ -1,37 +1,34 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
lib = File.expand_path("../lib", __FILE__)
|
|
3
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
-
require
|
|
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 =
|
|
12
|
-
gem.summary =
|
|
13
|
-
gem.homepage = "
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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 = [
|
|
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.
|
|
36
|
-
gem.
|
|
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
|
data/features/deploy.feature
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
@@ -1,5 +1,14 @@
|
|
|
1
|
+
require "shellwords"
|
|
2
|
+
|
|
1
3
|
Then(/^references in the remote repo are listed$/) do
|
|
2
|
-
expect(@output).to include(
|
|
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 |
|
|
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(
|
|
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(
|
|
53
|
-
expect(File.
|
|
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(
|
|
58
|
-
production = TestApp.test_app_path.join(
|
|
59
|
-
expect(File.
|
|
60
|
-
expect(File.
|
|
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(
|
|
65
|
-
expect(Dir.
|
|
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(
|
|
70
|
-
production = TestApp.test_app_path.join(
|
|
71
|
-
expect(File.
|
|
72
|
-
expect(File.
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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 #{
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
44
|
-
TestApp.copy_task_to_test_app(
|
|
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(
|
|
49
|
-
TestApp.copy_task_to_test_app(
|
|
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
|
data/features/support/env.rb
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
PROJECT_ROOT = File.expand_path(
|
|
2
|
-
VAGRANT_ROOT = File.join(PROJECT_ROOT,
|
|
3
|
-
VAGRANT_BIN = ENV[
|
|
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[
|
|
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
|
|
11
|
+
require_relative "../../spec/support/test_app"
|
|
@@ -1,22 +1,28 @@
|
|
|
1
1
|
module RemoteCommandHelpers
|
|
2
2
|
def test_dir_exists(path)
|
|
3
|
-
exists?(
|
|
3
|
+
exists?("d", path)
|
|
4
4
|
end
|
|
5
5
|
|
|
6
6
|
def test_symlink_exists(path)
|
|
7
|
-
exists?(
|
|
7
|
+
exists?("L", path)
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def test_file_exists(path)
|
|
11
|
-
exists?(
|
|
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
|
|
19
|
-
|
|
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[
|
|
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
|
-
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
data/lib/capistrano/all.rb
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
1
|
+
require "rake"
|
|
2
|
+
require "sshkit"
|
|
3
3
|
|
|
4
|
-
require
|
|
4
|
+
require "io/console"
|
|
5
5
|
|
|
6
6
|
Rake.application.options.trace = true
|
|
7
7
|
|
|
8
|
-
require
|
|
9
|
-
require
|
|
10
|
-
require
|
|
11
|
-
require
|
|
12
|
-
require
|
|
13
|
-
require
|
|
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
|