capistrano 3.7.0 → 3.17.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +113 -0
  3. data/.github/pull_request_template.md +0 -4
  4. data/.github/release-drafter.yml +17 -0
  5. data/.github/workflows/push.yml +12 -0
  6. data/.rubocop.yml +13 -0
  7. data/CHANGELOG.md +1 -484
  8. data/CONTRIBUTING.md +2 -0
  9. data/DEVELOPMENT.md +5 -6
  10. data/Dangerfile +1 -54
  11. data/Gemfile +39 -3
  12. data/LICENSE.txt +1 -1
  13. data/README.md +10 -4
  14. data/RELEASING.md +3 -3
  15. data/Rakefile +13 -5
  16. data/UPGRADING-3.7.md +1 -12
  17. data/capistrano.gemspec +9 -8
  18. data/features/deploy.feature +34 -1
  19. data/features/installation.feature +8 -3
  20. data/features/step_definitions/assertions.rb +31 -3
  21. data/features/step_definitions/cap_commands.rb +10 -0
  22. data/features/step_definitions/setup.rb +37 -1
  23. data/features/subdirectory.feature +9 -0
  24. data/features/support/remote_command_helpers.rb +4 -0
  25. data/features/support/vagrant_helpers.rb +15 -8
  26. data/lib/Capfile +0 -4
  27. data/lib/capistrano/application.rb +11 -3
  28. data/lib/capistrano/configuration/filter.rb +1 -1
  29. data/lib/capistrano/configuration/host_filter.rb +1 -2
  30. data/lib/capistrano/configuration/question.rb +22 -3
  31. data/lib/capistrano/configuration/role_filter.rb +1 -2
  32. data/lib/capistrano/configuration/scm_resolver.rb +8 -3
  33. data/lib/capistrano/configuration/server.rb +1 -0
  34. data/lib/capistrano/configuration/servers.rb +16 -8
  35. data/lib/capistrano/configuration/variables.rb +2 -2
  36. data/lib/capistrano/configuration.rb +7 -3
  37. data/lib/capistrano/defaults.rb +1 -1
  38. data/lib/capistrano/doctor/servers_doctor.rb +1 -1
  39. data/lib/capistrano/doctor/variables_doctor.rb +12 -3
  40. data/lib/capistrano/dsl/paths.rb +3 -16
  41. data/lib/capistrano/dsl.rb +10 -2
  42. data/lib/capistrano/i18n.rb +7 -2
  43. data/lib/capistrano/immutable_task.rb +3 -2
  44. data/lib/capistrano/scm/git.rb +34 -7
  45. data/lib/capistrano/scm/hg.rb +8 -1
  46. data/lib/capistrano/scm/svn.rb +9 -0
  47. data/lib/capistrano/scm/tasks/git.rake +10 -9
  48. data/lib/capistrano/scm/tasks/hg.rake +1 -1
  49. data/lib/capistrano/tasks/deploy.rake +22 -10
  50. data/lib/capistrano/templates/deploy.rb.erb +10 -4
  51. data/lib/capistrano/templates/stage.rb.erb +1 -1
  52. data/lib/capistrano/version.rb +1 -1
  53. data/spec/integration/dsl_spec.rb +5 -3
  54. data/spec/lib/capistrano/application_spec.rb +16 -40
  55. data/spec/lib/capistrano/configuration/filter_spec.rb +1 -1
  56. data/spec/lib/capistrano/configuration/host_filter_spec.rb +10 -5
  57. data/spec/lib/capistrano/configuration/plugin_installer_spec.rb +1 -1
  58. data/spec/lib/capistrano/configuration/question_spec.rb +47 -11
  59. data/spec/lib/capistrano/configuration/role_filter_spec.rb +2 -2
  60. data/spec/lib/capistrano/configuration/scm_resolver_spec.rb +55 -0
  61. data/spec/lib/capistrano/configuration/server_spec.rb +1 -1
  62. data/spec/lib/capistrano/configuration/servers_spec.rb +6 -5
  63. data/spec/lib/capistrano/configuration_spec.rb +2 -2
  64. data/spec/lib/capistrano/doctor/environment_doctor_spec.rb +1 -1
  65. data/spec/lib/capistrano/doctor/gems_doctor_spec.rb +1 -1
  66. data/spec/lib/capistrano/doctor/servers_doctor_spec.rb +1 -1
  67. data/spec/lib/capistrano/doctor/variables_doctor_spec.rb +9 -1
  68. data/spec/lib/capistrano/dsl/paths_spec.rb +30 -0
  69. data/spec/lib/capistrano/dsl/task_enhancements_spec.rb +6 -6
  70. data/spec/lib/capistrano/dsl_spec.rb +48 -7
  71. data/spec/lib/capistrano/immutable_task_spec.rb +1 -1
  72. data/spec/lib/capistrano/plugin_spec.rb +2 -2
  73. data/spec/lib/capistrano/scm/git_spec.rb +54 -1
  74. data/spec/lib/capistrano/scm/hg_spec.rb +6 -1
  75. data/spec/lib/capistrano/scm/svn_spec.rb +21 -0
  76. data/spec/lib/capistrano/version_validator_spec.rb +23 -0
  77. data/spec/spec_helper.rb +13 -0
  78. data/spec/support/Vagrantfile +1 -1
  79. data/spec/support/test_app.rb +28 -14
  80. metadata +20 -80
  81. data/.travis.yml +0 -26
data/CONTRIBUTING.md CHANGED
@@ -41,6 +41,8 @@ Also include in your report:
41
41
 
42
42
  If you are an experienced Ruby programmer, take a few minutes to get the Capistrano test suite running (see [DEVELOPMENT.md][]), and do what you can to get a test case written that fails. *This will be a huge help!*
43
43
 
44
+ If you think you may have discovered a security vulnerability in Capistrano, do not open a GitHub issue. Instead, please send a report to <security@capistranorb.com>.
45
+
44
46
  ## Requesting new features or improvements
45
47
 
46
48
  Capistrano continues to improve thanks to people like you! Feel free to open a GitHub issue for any or all of these ideas:
data/DEVELOPMENT.md CHANGED
@@ -28,11 +28,10 @@ Capistrano is a Ruby project, so we expect you to have a functioning Ruby enviro
28
28
 
29
29
  Make sure to install:
30
30
 
31
- * [Bundler](https://bundler.io/) 1.10.5 (see note)
31
+ * [Bundler](https://bundler.io/)
32
32
  * [Vagrant](https://www.vagrantup.com/)
33
33
  * [VirtualBox](https://www.virtualbox.org/wiki/Downloads) (or another [Vagrant-supported](https://docs.vagrantup.com/v2/getting-started/providers.html) VM host)
34
34
 
35
- *Note: As of this writing (December 2015), Vagrant does not work with Bundler > 1.10.5. If you have multiple versions of Bundler installed, use the special underscore command-line argument to force a compatible version, like this: `bundle _1.10.5_ exec rake features`.*
36
35
 
37
36
  ### Running tests
38
37
 
@@ -45,7 +44,7 @@ $ bundle install
45
44
  # Run the RSpec suite
46
45
  $ bundle exec rake spec
47
46
 
48
- # Run the Cucumber suite (requires Bundler 1.10.5)
47
+ # Run the Cucumber suite
49
48
  $ bundle exec rake features
50
49
 
51
50
  # Run the Cucumber suite and leave the VM running (faster for subsequent runs)
@@ -54,7 +53,7 @@ $ bundle exec rake features KEEP_RUNNING=1
54
53
 
55
54
  ### Report failing Cucumber features!
56
55
 
57
- Currently, the Capistrano Travis build does *not* run the Cucumber suite. This means it is possible for a failing Cucumber feature to sneak in without being noticed by our continuous integration checks.
56
+ Currently, the Capistrano CI build does *not* run the Cucumber suite. This means it is possible for a failing Cucumber feature to sneak in without being noticed by our continuous integration checks.
58
57
 
59
58
  **If you come across a failing Cucumber feature, this is a bug.** Please report it by opening a GitHub issue. Or even better: do your best to fix the feature and submit a pull request!
60
59
 
@@ -64,7 +63,7 @@ This project uses [RuboCop](https://github.com/bbatsov/rubocop) to enforce stand
64
63
 
65
64
  * Test that your contributions pass with `rake rubocop`
66
65
  * Rubocop is also run as part of the full test suite with `rake`
67
- * Note the Travis build will fail and your PR cannot be merged if Rubocop finds errors
66
+ * Note the CI build will fail and your PR cannot be merged if Rubocop finds errors
68
67
 
69
68
  ## Submitting a pull request
70
69
 
@@ -72,7 +71,7 @@ Pull requests are awesome, and if they arrive with decent tests, and conform to
72
71
 
73
72
  Your code should conform to these guidelines:
74
73
 
75
- * The code is MIT licenced, your code will fall under the same license if we merge it.
74
+ * The code is MIT licensed, your code will fall under the same license if we merge it.
76
75
  * We can't merge it without a [good commit message](http://robots.thoughtbot.com/5-useful-tips-for-a-better-commit-message). If you do this right, Github will use the commit message as the body of your pull request, double win.
77
76
  * If you are making an improvement/fix for an existing issue, make sure to mention the issue number (if we have not yet merged it )
78
77
  * Add an entry to the `CHANGELOG` under the `### master` section, but please don't mess with the version.
data/Dangerfile CHANGED
@@ -1,54 +1 @@
1
- # Adapted from https://github.com/ruby-grape/danger/blob/master/Dangerfile
2
- # Q: What is a Dangerfile, anyway? A: See http://danger.systems/
3
-
4
- # ------------------------------------------------------------------------------
5
- # Additional pull request data
6
- # ------------------------------------------------------------------------------
7
- project_name = github.pr_json["base"]["repo"]["name"]
8
- pr_number = github.pr_json["number"]
9
- pr_url = github.pr_json["_links"]["html"]["href"]
10
-
11
- # ------------------------------------------------------------------------------
12
- # What changed?
13
- # ------------------------------------------------------------------------------
14
- has_lib_changes = !git.modified_files.grep(/^lib/).empty?
15
- has_test_changes = !git.modified_files.grep(/^(features|spec)/).empty?
16
- has_changelog_changes = git.modified_files.include?("CHANGELOG.md")
17
-
18
- # ------------------------------------------------------------------------------
19
- # You've made changes to lib, but didn't write any tests?
20
- # ------------------------------------------------------------------------------
21
- if has_lib_changes && !has_test_changes
22
- warn("There are code changes, but no corresponding tests. "\
23
- "Please include tests if this PR introduces any modifications in "\
24
- "#{project_name}'s behavior.",
25
- :sticky => false)
26
- end
27
-
28
- # ------------------------------------------------------------------------------
29
- # Have you updated CHANGELOG.md?
30
- # ------------------------------------------------------------------------------
31
- if !has_changelog_changes && has_lib_changes
32
- markdown <<-MARKDOWN
33
- Here's an example of a CHANGELOG.md entry (place it immediately under the `* Your contribution here!` line):
34
-
35
- ```markdown
36
- * [##{pr_number}](#{pr_url}): #{github.pr_title} - [@#{github.pr_author}](https://github.com/#{github.pr_author}).
37
- ```
38
- MARKDOWN
39
- warn("Please update CHANGELOG.md with a description of your changes. "\
40
- "If this PR is not a user-facing change (e.g. just refactoring), "\
41
- "you can disregard this.", :sticky => false)
42
- end
43
-
44
- # ------------------------------------------------------------------------------
45
- # Did you remove the CHANGELOG's "Your contribution here!" line?
46
- # ------------------------------------------------------------------------------
47
- if has_changelog_changes
48
- unless IO.read("CHANGELOG.md") =~ /^\* Your contribution here/i
49
- fail(
50
- "Please put the `* Your contribution here!` line back into CHANGELOG.md.",
51
- :sticky => false
52
- )
53
- end
54
- end
1
+ danger.import_dangerfile(github: "capistrano/danger", branch: "no-changelog")
data/Gemfile CHANGED
@@ -3,8 +3,44 @@ source "https://rubygems.org"
3
3
  # Specify your gem's dependencies in capistrano.gemspec
4
4
  gemspec
5
5
 
6
+ gem "mocha"
7
+ gem "rspec"
8
+ gem "rspec-core", "~> 3.4.4"
9
+
6
10
  group :cucumber do
7
- gem "cucumber"
8
- gem "rspec"
9
- gem "rspec-core", "~> 3.4.4"
11
+ # Latest versions of cucumber don't support Ruby < 2.1
12
+ # rubocop:disable Bundler/DuplicatedGem
13
+ if Gem::Requirement.new("< 2.1").satisfied_by?(Gem::Version.new(RUBY_VERSION))
14
+ gem "cucumber", "< 3.0.1"
15
+ else
16
+ gem "cucumber"
17
+ end
18
+ # rubocop:enable Bundler/DuplicatedGem
19
+ end
20
+
21
+ # Latest versions of net-ssh don't support Ruby < 2.2.6
22
+ if Gem::Requirement.new("< 2.2.6").satisfied_by?(Gem::Version.new(RUBY_VERSION))
23
+ gem "net-ssh", "< 5.0.0"
24
+ end
25
+
26
+ # Latest versions of public_suffix don't support Ruby < 2.1
27
+ if Gem::Requirement.new("< 2.1").satisfied_by?(Gem::Version.new(RUBY_VERSION))
28
+ gem "public_suffix", "< 3.0.0"
29
+ end
30
+
31
+ # Latest versions of i18n don't support Ruby < 2.4
32
+ if Gem::Requirement.new("< 2.4").satisfied_by?(Gem::Version.new(RUBY_VERSION))
33
+ gem "i18n", "< 1.3.0"
34
+ end
35
+
36
+ # Latest versions of rake don't support Ruby < 2.2
37
+ if Gem::Requirement.new("< 2.2").satisfied_by?(Gem::Version.new(RUBY_VERSION))
38
+ gem "rake", "< 13.0.0"
39
+ end
40
+
41
+ # We only run danger and rubocop on a new-ish ruby; no need to install them otherwise
42
+ if Gem::Requirement.new("> 2.4").satisfied_by?(Gem::Version.new(RUBY_VERSION))
43
+ gem "danger"
44
+ gem "psych", "< 4" # Ensures rubocop works on Ruby 3.1
45
+ gem "rubocop", "0.48.1"
10
46
  end
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License (MIT)
2
2
 
3
- Copyright (c) 2012-2016 Tom Clements, Lee Hambley
3
+ Copyright (c) 2012-2020 Tom Clements, Lee Hambley
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  # Capistrano: A deployment automation tool built on Ruby, Rake, and SSH.
3
3
 
4
- [![Gem Version](https://badge.fury.io/rb/capistrano.svg)](http://badge.fury.io/rb/capistrano) [![Build Status](https://travis-ci.org/capistrano/capistrano.svg?branch=master)](https://travis-ci.org/capistrano/capistrano) [![Code Climate](https://codeclimate.com/github/capistrano/capistrano/badges/gpa.svg)](https://codeclimate.com/github/capistrano/capistrano) [![CodersClan](https://img.shields.io/badge/get-support-blue.svg)](http://codersclan.net/?repo_id=325&source=small)
4
+ [![Gem Version](https://badge.fury.io/rb/capistrano.svg)](http://badge.fury.io/rb/capistrano) [![Build Status](https://circleci.com/gh/capistrano/capistrano/tree/master.svg?style=shield)](https://app.circleci.com/pipelines/github/capistrano/capistrano?branch=master) [![Code Climate](https://codeclimate.com/github/capistrano/capistrano/badges/gpa.svg)](https://codeclimate.com/github/capistrano/capistrano) [![CodersClan](https://img.shields.io/badge/get-support-blue.svg)](http://codersclan.net/?repo_id=325&source=small)
5
5
 
6
6
  Capistrano is a framework for building automated deployment scripts. Although Capistrano itself is written in Ruby, it can easily be used to deploy projects of any language or framework, be it Rails, Java, or PHP.
7
7
 
@@ -88,6 +88,10 @@ Likewise, your server(s) will likely need supporting software installed before y
88
88
 
89
89
  Capistrano is designed to deploy using a single, non-privileged SSH user, using a *non-interactive* SSH session. If your deployment requires `sudo`, interactive prompts, authenticating as one user but running commands as another, you can probably accomplish this with Capistrano, but it may be difficult. Your automated deployments will be much smoother if you can avoid such requirements.
90
90
 
91
+ #### Shells
92
+
93
+ Capistrano 3 expects a POSIX shell like Bash or Sh. Shells like tcsh, csh, and such may work, but probably will not.
94
+
91
95
  ## Quick start
92
96
 
93
97
  ### Requirements
@@ -99,11 +103,11 @@ Capistrano is designed to deploy using a single, non-privileged SSH user, using
99
103
 
100
104
  ### Install the Capistrano gem
101
105
 
102
- Add Capistrano to your project's Gemfile:
106
+ Add Capistrano to your project's Gemfile using `require: false`:
103
107
 
104
108
  ``` ruby
105
109
  group :development do
106
- gem "capistrano", "~> 3.7"
110
+ gem "capistrano", "~> 3.17", require: false
107
111
  end
108
112
  ```
109
113
 
@@ -186,6 +190,8 @@ Related GitHub repositories:
186
190
 
187
191
  GitHub issues are for bug reports and feature requests. Please refer to the [CONTRIBUTING](CONTRIBUTING.md) document for guidelines on submitting GitHub issues.
188
192
 
193
+ If you think you may have discovered a security vulnerability in Capistrano, do not open a GitHub issue. Instead, please send a report to <security@capistranorb.com>.
194
+
189
195
  ## How to contribute
190
196
 
191
197
  Contributions to Capistrano, in the form of code, documentation or idea, are gladly accepted. Read the [DEVELOPMENT](DEVELOPMENT.md) document to learn how to hack on Capistrano's code, run the tests, and contribute your first pull request.
@@ -194,7 +200,7 @@ Contributions to Capistrano, in the form of code, documentation or idea, are gla
194
200
 
195
201
  MIT License (MIT)
196
202
 
197
- Copyright (c) 2012-2015 Tom Clements, Lee Hambley
203
+ Copyright (c) 2012-2020 Tom Clements, Lee Hambley
198
204
 
199
205
  Permission is hereby granted, free of charge, to any person obtaining a copy
200
206
  of this software and associated documentation files (the "Software"), to deal
data/RELEASING.md CHANGED
@@ -11,7 +11,7 @@
11
11
  2. **Ensure all tests are passing by running `rake spec` and `rake features`.**
12
12
  3. Determine which would be the correct next version number according to [semver](http://semver.org/).
13
13
  4. Update the version in `./lib/capistrano/version.rb`.
14
- 4. Update the version in the `./README.md` Gemfile example (`gem "capistrano", "~> X.Y"`).
15
- 5. Update the `CHANGELOG`.
16
- 6. Commit the changelog and version in a single commit, the message should be "Preparing vX.Y.Z"
14
+ 5. Update the version in the `./README.md` Gemfile example (`gem "capistrano", "~> X.Y"`).
15
+ 6. Commit the `version.rb` and `README.md` changes in a single commit, the message should be "Release vX.Y.Z"
17
16
  7. Run `rake release`; this will tag, push to GitHub, and publish to rubygems.org.
17
+ 8. Update the draft release on the [GitHub releases page](https://github.com/capistrano/capistrano/releases) to point to the new tag and publish the release
data/Rakefile CHANGED
@@ -1,12 +1,20 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "cucumber/rake/task"
3
3
  require "rspec/core/rake_task"
4
- require "rubocop/rake_task"
5
4
 
6
- task default: [:spec, :rubocop]
7
- RSpec::Core::RakeTask.new
5
+ begin
6
+ require "rubocop/rake_task"
7
+ desc "Run RuboCop checks"
8
+ RuboCop::RakeTask.new
9
+ task default: %i(spec rubocop)
10
+ rescue LoadError
11
+ task default: :spec
12
+ end
8
13
 
14
+ RSpec::Core::RakeTask.new
9
15
  Cucumber::Rake::Task.new(:features)
10
16
 
11
- desc "Run RuboCop checks"
12
- RuboCop::RakeTask.new
17
+ Rake::Task["release"].enhance do
18
+ puts "Don't forget to publish the release on GitHub!"
19
+ system "open https://github.com/capistrano/capistrano/releases"
20
+ end
data/UPGRADING-3.7.md CHANGED
@@ -1,16 +1,5 @@
1
1
  # Capistrano 3.7.0 upgrade guide
2
2
 
3
- Capistrano 3.7.0 has not yet been released. This guide serves as a preview of
4
- what is *planned* for 3.7.0, so that you can be prepared to update your
5
- Capistrano deployment if necessary once it becomes available.
6
-
7
- If you wish to try the new 3.7.0 behavior today, you can do so by using the
8
- `master` branch in your Gemfile:
9
-
10
- ```ruby
11
- gem "capistrano", :github => "capistrano/capistrano"
12
- ```
13
-
14
3
  ## The :scm variable is deprecated
15
4
 
16
5
  Up until now, Capistrano's SCM was configured using the `:scm` variable:
@@ -23,7 +12,7 @@ set :scm, :svn
23
12
  To avoid deprecation warnings:
24
13
 
25
14
  1. Remove `set :scm, ...` from your Capistrano configuration.
26
- 2. Add *one* of the following SCM declarations to your `Capfile`:
15
+ 2. Add *one* of the following SCM declarations to your `Capfile` after `require "capistrano/deploy"`:
27
16
 
28
17
  ```ruby
29
18
  # To use Git
data/capistrano.gemspec CHANGED
@@ -1,4 +1,5 @@
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
5
  require "capistrano/version"
@@ -10,8 +11,14 @@ Gem::Specification.new do |gem|
10
11
  gem.email = ["seenmyfate@gmail.com", "lee.hambley@gmail.com"]
11
12
  gem.description = "Capistrano is a utility and framework for executing commands in parallel on multiple remote machines, via SSH."
12
13
  gem.summary = "Capistrano - Welcome to easy deployment with Ruby over SSH"
13
- gem.homepage = "http://capistranorb.com/"
14
-
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
+ }
15
22
  gem.files = `git ls-files -z`.split("\x0").reject { |f| f =~ /^docs/ }
16
23
  gem.executables = %w(cap capify)
17
24
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
@@ -24,10 +31,4 @@ Gem::Specification.new do |gem|
24
31
  gem.add_dependency "i18n"
25
32
  gem.add_dependency "rake", ">= 10.0.0"
26
33
  gem.add_dependency "sshkit", ">= 1.9.0"
27
- gem.add_dependency "capistrano-harrow"
28
-
29
- gem.add_development_dependency "danger"
30
- gem.add_development_dependency "mocha"
31
- gem.add_development_dependency "rspec"
32
- gem.add_development_dependency "rubocop"
33
34
  end
@@ -7,7 +7,6 @@ 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
11
10
  And git wrapper permissions are 0700
12
11
 
13
12
  Scenario: Creating the directory structure
@@ -53,3 +52,37 @@ Feature: Deploy
53
52
  When I run cap "deploy:symlink:release"
54
53
  Then the current directory will be a symlink to the release
55
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
+
@@ -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
@@ -1,10 +1,14 @@
1
+ require "shellwords"
2
+
1
3
  Then(/^references in the remote repo are listed$/) do
2
4
  expect(@output).to include("refs/heads/master")
3
5
  end
4
6
 
5
7
  Then(/^git wrapper permissions are 0700$/) do
6
- permissions_test = %Q([ $(stat -c "%a" #{TestApp.git_wrapper_path}) == "700" ])
7
- expect(vagrant_cli_command("ssh -c '#{permissions_test}'")).to be_success
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
8
12
  end
9
13
 
10
14
  Then(/^the shared path is created$/) do
@@ -15,6 +19,18 @@ Then(/^the releases path is created$/) do
15
19
  run_vagrant_command(test_dir_exists(TestApp.releases_path))
16
20
  end
17
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
+
18
34
  Then(/^directories in :linked_dirs are created in shared$/) do
19
35
  TestApp.linked_dirs.each do |dir|
20
36
  run_vagrant_command(test_dir_exists(TestApp.shared_path.join(dir)))
@@ -50,7 +66,7 @@ Then(/^directory symlinks are created in the new release$/) do
50
66
  end
51
67
 
52
68
  Then(/^the current directory will be a symlink to the release$/) do
53
- run_vagrant_command(test_symlink_exists(TestApp.current_path))
69
+ run_vagrant_command(exists?("e", TestApp.current_path))
54
70
  end
55
71
 
56
72
  Then(/^the deploy\.rb file is created$/) do
@@ -122,3 +138,15 @@ end
122
138
  Then(/doesn't contain "([^"]*)" in the output/) do |expected|
123
139
  expect(@output).not_to include(expected)
124
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
@@ -9,3 +13,9 @@ end
9
13
  When(/^I run "(.*?)"$/) do |command|
10
14
  @success, @output = TestApp.run(command)
11
15
  end
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,6 +2,10 @@ 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
7
11
  vagrant_cli_command("up")
@@ -17,10 +21,16 @@ end
17
21
 
18
22
  Given(/^file "(.*?)" exists in shared path$/) do |file|
19
23
  file_shared_path = TestApp.shared_path.join(file)
20
- run_vagrant_command("mkdir -p #{TestApp.shared_path}")
24
+ run_vagrant_command("mkdir -p #{file_shared_path.dirname}")
21
25
  run_vagrant_command("touch #{file_shared_path}")
22
26
  end
23
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
+
24
34
  Given(/^file "(.*?)" does not exist in shared path$/) do |file|
25
35
  file_shared_path = TestApp.shared_path.join(file)
26
36
  run_vagrant_command("mkdir -p #{TestApp.shared_path}")
@@ -56,3 +66,29 @@ end
56
66
  Given(/^a stage file named (.+)$/) do |filename|
57
67
  TestApp.write_local_stage_file(filename)
58
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)}")
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
@@ -15,6 +15,10 @@ module RemoteCommandHelpers
15
15
  %Q{[ -#{type} "#{path}" ]}
16
16
  end
17
17
 
18
+ def symlinked?(symlink_path, target_path)
19
+ "[ #{symlink_path} -ef #{target_path} ]"
20
+ end
21
+
18
22
  def safely_remove_file(_path)
19
23
  run_vagrant_command("rm #{test_file}")
20
24
  rescue
@@ -1,4 +1,4 @@
1
- require "English"
1
+ require "open3"
2
2
 
3
3
  module VagrantHelpers
4
4
  extend self
@@ -16,19 +16,26 @@ module VagrantHelpers
16
16
 
17
17
  def vagrant_cli_command(command)
18
18
  puts "[vagrant] #{command}"
19
- Dir.chdir(VAGRANT_ROOT) do
20
- `#{VAGRANT_BIN} #{command} 2>&1`.split("\n").each do |line|
21
- puts "[vagrant] #{line}"
22
- end
19
+ stdout, stderr, status = Dir.chdir(VAGRANT_ROOT) do
20
+ Open3.capture3("#{VAGRANT_BIN} #{command}")
23
21
  end
24
- $CHILD_STATUS
22
+
23
+ (stdout + stderr).each_line { |line| puts "[vagrant] #{line}" }
24
+
25
+ [stdout, stderr, status]
25
26
  end
26
27
 
27
28
  def run_vagrant_command(command)
28
- status = vagrant_cli_command("ssh -c #{command.inspect}")
29
- return true if status.success?
29
+ stdout, stderr, status = vagrant_cli_command("ssh -c #{command.inspect}")
30
+ return [stdout, stderr] if status.success?
30
31
  raise VagrantSSHCommandError, status
31
32
  end
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
32
39
  end
33
40
 
34
41
  World(VagrantHelpers)
data/lib/Capfile CHANGED
@@ -1,7 +1,3 @@
1
1
  #!/usr/bin/env cap
2
2
  include Capistrano::DSL
3
3
  require "capistrano/install"
4
-
5
- require "capistrano/harrow"
6
- require "capistrano/harrow/plugin"
7
- install_plugin Capistrano::Harrow::Plugin
@@ -2,7 +2,7 @@ module Capistrano
2
2
  class Application < Rake::Application
3
3
  def initialize
4
4
  super
5
- @rakefiles = %w{capfile Capfile capfile.rb Capfile.rb} << capfile
5
+ @rakefiles = %w{capfile Capfile capfile.rb Capfile.rb}
6
6
  end
7
7
 
8
8
  def name
@@ -76,6 +76,15 @@ module Capistrano
76
76
  end
77
77
  end
78
78
 
79
+ # allows the `cap install` task to load without a capfile
80
+ def find_rakefile_location
81
+ if (location = super).nil?
82
+ [capfile, Dir.pwd]
83
+ else
84
+ location
85
+ end
86
+ end
87
+
79
88
  private
80
89
 
81
90
  def backtrace_pattern
@@ -87,7 +96,7 @@ module Capistrano
87
96
  end
88
97
 
89
98
  def load_imports
90
- if options.show_tasks
99
+ if options.show_tasks && Rake::Task.task_defined?("load:defaults")
91
100
  invoke "load:defaults"
92
101
  set(:stage, "")
93
102
  Dir[deploy_config_path].each { |f| add_import f }
@@ -96,7 +105,6 @@ module Capistrano
96
105
  super
97
106
  end
98
107
 
99
- # allows the `cap install` task to load without a capfile
100
108
  def capfile
101
109
  File.expand_path(File.join(File.dirname(__FILE__), "..", "Capfile"))
102
110
  end
@@ -8,7 +8,7 @@ module Capistrano
8
8
  class Configuration
9
9
  class Filter
10
10
  def initialize(type, values=nil)
11
- raise "Invalid filter type #{type}" unless [:host, :role].include? type
11
+ raise "Invalid filter type #{type}" unless %i(host role).include? type
12
12
  av = Array(values)
13
13
  @strategy = if av.empty? then EmptyFilter.new
14
14
  elsif av.include?(:all) || av.include?("all") then NullFilter.new