capistrano-3.10.0/0000755000004100000410000000000013175754131013775 5ustar www-datawww-datacapistrano-3.10.0/Rakefile0000644000004100000410000000040413175754131015440 0ustar www-datawww-datarequire "bundler/gem_tasks" require "cucumber/rake/task" require "rspec/core/rake_task" require "rubocop/rake_task" task default: %i(spec rubocop) RSpec::Core::RakeTask.new Cucumber::Rake::Task.new(:features) desc "Run RuboCop checks" RuboCop::RakeTask.new capistrano-3.10.0/bin/0000755000004100000410000000000013175754131014545 5ustar www-datawww-datacapistrano-3.10.0/bin/capify0000755000004100000410000000036213175754131015747 0ustar www-datawww-data#!/usr/bin/env ruby puts "-" * 80 puts "Capistrano 3.x is incompatible with Capistrano 2.x. " puts puts "This command has become `cap install` in Capistrano 3.x" puts puts "For more information see http://www.capistranorb.com/" puts "-" * 80 capistrano-3.10.0/bin/cap0000755000004100000410000000011513175754131015233 0ustar www-datawww-data#!/usr/bin/env ruby require "capistrano/all" Capistrano::Application.new.run capistrano-3.10.0/capistrano.gemspec0000644000004100000410000000240113175754131017502 0ustar www-datawww-data# -*- encoding: utf-8 -*- lib = File.expand_path("../lib", __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require "capistrano/version" Gem::Specification.new do |gem| gem.name = "capistrano" gem.version = Capistrano::VERSION gem.authors = ["Tom Clements", "Lee Hambley"] gem.email = ["seenmyfate@gmail.com", "lee.hambley@gmail.com"] gem.description = "Capistrano is a utility and framework for executing commands in parallel on multiple remote machines, via SSH." gem.summary = "Capistrano - Welcome to easy deployment with Ruby over SSH" gem.homepage = "http://capistranorb.com/" gem.files = `git ls-files -z`.split("\x0").reject { |f| f =~ /^docs/ } gem.executables = %w(cap capify) gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) gem.require_paths = ["lib"] gem.licenses = ["MIT"] gem.required_ruby_version = ">= 2.0" gem.add_dependency "airbrussh", ">= 1.0.0" gem.add_dependency "i18n" gem.add_dependency "rake", ">= 10.0.0" gem.add_dependency "sshkit", ">= 1.9.0" gem.add_development_dependency "danger" gem.add_development_dependency "mocha" gem.add_development_dependency "rspec" gem.add_development_dependency "rubocop", "0.48.1" end capistrano-3.10.0/Gemfile0000644000004100000410000000026513175754131015273 0ustar www-datawww-datasource "https://rubygems.org" # Specify your gem's dependencies in capistrano.gemspec gemspec group :cucumber do gem "cucumber" gem "rspec" gem "rspec-core", "~> 3.4.4" end capistrano-3.10.0/DEVELOPMENT.md0000644000004100000410000001737613175754131016117 0ustar www-datawww-dataThanks for helping build Capistrano! Here are the development practices followed by our community. * [Who can help](#who-can-help) * [Contributing documentation](#contributing-documentation) * [Setting up your development environment](#setting-up-your-development-environment) * [Coding guidelines](#coding-guidelines) * [Submitting a pull request](#submitting-a-pull-request) * [Managing GitHub issues](#managing-github-issues) * [Reviewing and merging pull requests](#reviewing-and-merging-pull-requests) ## Who can help Everyone can help improve Capistrano. There are ways to contribute even if you aren’t a Ruby programmer. Here’s what you can do to help the project: * adding to or fixing typos/quality in documentation * adding failing tests for reported bugs * writing code (no contribution is too small!) * reviewing pull requests and suggesting improvements * reporting bugs or suggesting new features (see [CONTRIBUTING.md][]) ## Contributing documentation Improvements and additions to Capistrano's documentation are very much appreciated. The official documention is stored in the `docs/` directory as Markdown files. These files are used to automatically generate the [capistranorb.com](http://capistranorb.com/) website, which is hosted by GitHub Pages. Feel free to make changes to this documentation as you see fit. Before opening a pull request, make sure your documentation renders correctly by previewing the website in your local environment. Refer to [docs/README.md][] for instructions. ## Setting up your development environment Capistrano is a Ruby project, so we expect you to have a functioning Ruby environment. To hack on Capistrano you will further need some specialized tools to run its test suite. Make sure to install: * [Bundler](https://bundler.io/) * [Vagrant](https://www.vagrantup.com/) * [VirtualBox](https://www.virtualbox.org/wiki/Downloads) (or another [Vagrant-supported](https://docs.vagrantup.com/v2/getting-started/providers.html) VM host) ### Running tests Capistrano has two test suites: an RSpec suite and a Cucumber suite. The RSpec suite handles quick feedback unit specs. The Cucumber suite is an integration suite that uses Vagrant to deploy to a real virtual server. ``` # Ensure all dependencies are installed $ bundle install # Run the RSpec suite $ bundle exec rake spec # Run the Cucumber suite $ bundle exec rake features # Run the Cucumber suite and leave the VM running (faster for subsequent runs) $ bundle exec rake features KEEP_RUNNING=1 ``` ### Report failing Cucumber features! 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. **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! ## Coding guidelines This project uses [RuboCop](https://github.com/bbatsov/rubocop) to enforce standard Ruby coding guidelines. * Test that your contributions pass with `rake rubocop` * Rubocop is also run as part of the full test suite with `rake` * Note the Travis build will fail and your PR cannot be merged if Rubocop finds errors ## Submitting a pull request Pull requests are awesome, and if they arrive with decent tests, and conform to the guidelines below, we'll merge them in as soon as possible, we'll let you know which release we're planning them for (we adhere to [semver](http://semver.org/) so please don't be upset if we plan your changes for a later release). Your code should conform to these guidelines: * The code is MIT licensed, your code will fall under the same license if we merge it. * 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. * 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 ) * Add an entry to the `CHANGELOG` under the `### master` section, but please don't mess with the version. * If you add a new feature, please make sure to document it by modifying the appropriate Markdown files in `docs/` (see [contributing documentation](#contributing-documentation), above). If it's a simple feature, or a new variable, or something changed, it may be appropriate simply to document it in the generated `Capfile` or `deploy.rb`, or in the `README`. * Take care to squash your commit into one single commit with a good message, it saves us a lot of work in maintaining the CHANGELOG if we can generate it from the commit messages between the release tags! * Tests! It's tricky to test some parts of Capistrano, but do your best, it might just serve as a starting point for us to build a reliable test on top of, and help us understand where you are coming from. ## Managing GitHub issues The Capistrano maintainers will do our best to review all GitHub issues. Here’s how we manage issues: 1. Issues will be acknowledged with an appropriate label (see below). 2. Issues that are duplicates, spam, or irrelevant (e.g. wrong project), or are questions better suited for Stack Overflow (see [CONTRIBUTING.md][]) will be closed. 3. Once an issue is fixed or resolved in an upcoming Capistrano release, it will be closed and assigned to a GitHub milestone for that upcoming version number. The maintainers do not have time to fix every issue ourselves, but we will gladly accept pull requests, especially for issues labeled as "you can help" (see below). ### Issue labels Capistrano uses these GitHub labels to categorize issues: * **bug?** – Could be a bug (not reproducible or might be user error) * **confirmed bug** – Definitely a bug * **new feature** – A request for Capistrano to add a feature or work differently * **chore** – A TODO that is neither a bug nor a feature (e.g. improve docs, CI infrastructure, etc.) Also, the Capistrano team will sometimes add these labels to facilitate communication and encourage community feedback: * **discuss!** – The Capistrano team wants more feedback from the community on this issue; e.g. how a new feature should work, or whether a bug is serious or not. * **you can help!** – We want the community to help by submitting a pull request to solve a bug or add a feature. If you are looking for ways to contribute to Capistrano, start here! * **needs more info** – We need more info from the person who opened the issue; e.g. steps to reproduce a bug, clarification on a desired feature, etc. *These labels were inspired by Daniel Doubrovkine’s [2014 Golden Gate Ruby Conference talk](http://confreaks.tv/videos/gogaruco2014-taking-over-someone-else-s-open-source-projects).* ## Reviewing and merging pull requests Pull requests and issues follow similar workflows. Before merging a pull request, the Capistrano maintainers will check that these requirements are met: * All CI checks pass * Significant changes in behavior or fixes mentioned in the CHANGELOG * Clean commit history * New features are documented * Code changes/additions are tested If any of these are missing, the **needs more info** label is assigned to the pull request to indicate the PR is incomplete. Merging a pull request is a decision entrusted to the maintainers of the Capistrano project. Any maintainer is free to merge a pull request if they feel the PR meets the above requirements and is in the best interest of the Capistrano community. After a pull request is merged, it is assigned to a GitHub milestone for the upcoming version number. [CONTRIBUTING.md]: https://github.com/capistrano/capistrano/blob/master/CONTRIBUTING.md [docs/README.md]: https://github.com/capistrano/capistrano/blob/master/docs/README.md capistrano-3.10.0/features/0000755000004100000410000000000013175754131015613 5ustar www-datawww-datacapistrano-3.10.0/features/step_definitions/0000755000004100000410000000000013175754131021161 5ustar www-datawww-datacapistrano-3.10.0/features/step_definitions/setup.rb0000644000004100000410000000511113175754131022644 0ustar www-datawww-dataGiven(/^a test app with the default configuration$/) do TestApp.install end Given(/^a test app without any configuration$/) do TestApp.create_test_app end Given(/^servers with the roles app and web$/) do begin vagrant_cli_command("up") rescue nil end end Given(/^a linked file "(.*?)"$/) do |file| # ignoring other linked files TestApp.append_to_deploy_file("set :linked_files, ['#{file}']") end Given(/^file "(.*?)" exists in shared path$/) do |file| file_shared_path = TestApp.shared_path.join(file) run_vagrant_command("mkdir -p #{file_shared_path.dirname}") run_vagrant_command("touch #{file_shared_path}") end Given(/^all linked files exists in shared path$/) do TestApp.linked_files.each do |linked_file| step %Q{file "#{linked_file}" exists in shared path} end end Given(/^file "(.*?)" does not exist in shared path$/) do |file| file_shared_path = TestApp.shared_path.join(file) run_vagrant_command("mkdir -p #{TestApp.shared_path}") run_vagrant_command("touch #{file_shared_path} && rm #{file_shared_path}") end Given(/^a custom task to generate a file$/) do TestApp.copy_task_to_test_app("spec/support/tasks/database.rake") end Given(/^a task which executes as root$/) do TestApp.copy_task_to_test_app("spec/support/tasks/root.rake") end Given(/config stage file has line "(.*?)"/) do |line| TestApp.append_to_deploy_file(line) end Given(/^the configuration is in a custom location$/) do TestApp.move_configuration_to_custom_location("app") end Given(/^a custom task that will simulate a failure$/) do safely_remove_file(TestApp.shared_path.join("failed")) TestApp.copy_task_to_test_app("spec/support/tasks/fail.rake") end Given(/^a custom task to run in the event of a failure$/) do safely_remove_file(TestApp.shared_path.join("failed")) TestApp.copy_task_to_test_app("spec/support/tasks/failed.rake") end Given(/^a stage file named (.+)$/) do |filename| TestApp.write_local_stage_file(filename) end Given(/^I make (\d+) deployments$/) do |count| step "all linked files exists in shared path" @release_paths = (1..count.to_i).map do TestApp.cap("deploy") stdout, _stderr = run_vagrant_command("readlink #{TestApp.current_path}") stdout.strip end end Given(/^(\d+) valid existing releases$/) do |num| a_day = 86_400 # in seconds offset = -(a_day * num.to_i) num.to_i.times do run_vagrant_command("mkdir -p #{TestApp.release_path(TestApp.timestamp(offset))}") offset += a_day end end Given(/^an invalid release named "(.+)"$/) do |filename| run_vagrant_command("mkdir -p #{TestApp.release_path(filename)}") end capistrano-3.10.0/features/step_definitions/assertions.rb0000644000004100000410000001107413175754131023703 0ustar www-datawww-datarequire "shellwords" Then(/^references in the remote repo are listed$/) do expect(@output).to include("refs/heads/master") end Then(/^git wrapper permissions are 0700$/) do permissions_test = %Q([ $(stat -c "%a" #{TestApp.git_wrapper_path.shellescape}) == "700" ]) _stdout, _stderr, status = vagrant_cli_command("ssh -c #{permissions_test.shellescape}") expect(status).to be_success end Then(/^the shared path is created$/) do run_vagrant_command(test_dir_exists(TestApp.shared_path)) end Then(/^the releases path is created$/) do run_vagrant_command(test_dir_exists(TestApp.releases_path)) end Then(/^(\d+) valid releases are kept/) do |num| test = %Q([ $(ls -g #{TestApp.releases_path} | grep -E '[0-9]{14}' | wc -l) == "#{num}" ]) _, _, status = vagrant_cli_command("ssh -c #{test.shellescape}") expect(status).to be_success end Then(/^the invalid (.+) release is ignored$/) do |filename| test = "ls -g #{TestApp.releases_path} | grep #{filename}" _, _, status = vagrant_cli_command("ssh -c #{test.shellescape}") expect(status).to be_success end Then(/^directories in :linked_dirs are created in shared$/) do TestApp.linked_dirs.each do |dir| run_vagrant_command(test_dir_exists(TestApp.shared_path.join(dir))) end end Then(/^directories referenced in :linked_files are created in shared$/) do dirs = TestApp.linked_files.map { |path| TestApp.shared_path.join(path).dirname } dirs.each do |dir| run_vagrant_command(test_dir_exists(dir)) end end Then(/^the repo is cloned$/) do run_vagrant_command(test_dir_exists(TestApp.repo_path)) end Then(/^the release is created$/) do run_vagrant_command("ls -g #{TestApp.releases_path}") end Then(/^file symlinks are created in the new release$/) do TestApp.linked_files.each do |file| run_vagrant_command(test_symlink_exists(TestApp.current_path.join(file))) end end Then(/^directory symlinks are created in the new release$/) do pending TestApp.linked_dirs.each do |dir| run_vagrant_command(test_symlink_exists(TestApp.release_path.join(dir))) end end Then(/^the current directory will be a symlink to the release$/) do run_vagrant_command(exists?("e", TestApp.current_path)) end Then(/^the deploy\.rb file is created$/) do file = TestApp.test_app_path.join("config/deploy.rb") expect(File.exist?(file)).to be true end Then(/^the default stage files are created$/) do staging = TestApp.test_app_path.join("config/deploy/staging.rb") production = TestApp.test_app_path.join("config/deploy/production.rb") expect(File.exist?(staging)).to be true expect(File.exist?(production)).to be true end Then(/^the tasks folder is created$/) do path = TestApp.test_app_path.join("lib/capistrano/tasks") expect(Dir.exist?(path)).to be true end Then(/^the specified stage files are created$/) do qa = TestApp.test_app_path.join("config/deploy/qa.rb") production = TestApp.test_app_path.join("config/deploy/production.rb") expect(File.exist?(qa)).to be true expect(File.exist?(production)).to be true end Then(/^it creates the file with the remote_task prerequisite$/) do TestApp.linked_files.each do |file| run_vagrant_command(test_file_exists(TestApp.shared_path.join(file))) end end Then(/^it will not recreate the file$/) do # end Then(/^the task is successful$/) do expect(@success).to be true end Then(/^the task fails$/) do expect(@success).to be_falsey end Then(/^the failure task will run$/) do failed = TestApp.shared_path.join("failed") run_vagrant_command(test_file_exists(failed)) end Then(/^the failure task will not run$/) do failed = TestApp.shared_path.join("failed") expect { run_vagrant_command(test_file_exists(failed)) } .to raise_error(VagrantHelpers::VagrantSSHCommandError) end When(/^an error is raised$/) do error = TestApp.shared_path.join("fail") run_vagrant_command(test_file_exists(error)) end Then(/contains "([^"]*)" in the output/) do |expected| expect(@output).to include(expected) end Then(/the output matches "([^"]*)" followed by "([^"]*)"/) do |expected, followedby| expect(@output).to match(/#{expected}.*#{followedby}/m) end Then(/doesn't contain "([^"]*)" in the output/) do |expected| expect(@output).not_to include(expected) end Then(/the current symlink points to the previous release/) do previous_release_path = @release_paths[-2] run_vagrant_command(symlinked?(TestApp.current_path, previous_release_path)) end Then(/^the current symlink points to that specific release$/) do specific_release_path = TestApp.releases_path.join(@rollback_release) run_vagrant_command(symlinked?(TestApp.current_path, specific_release_path)) end capistrano-3.10.0/features/step_definitions/cap_commands.rb0000644000004100000410000000113213175754131024127 0ustar www-datawww-dataWhen(/^I run cap "(.*?)"$/) do |task| @success, @output = TestApp.cap(task) end When(/^I run cap "(.*?)" within the "(.*?)" directory$/) do |task, directory| @success, @output = TestApp.cap(task, directory) end When(/^I run cap "(.*?)" as part of a release$/) do |task| TestApp.cap("deploy:new_release_path #{task}") end When(/^I run "(.*?)"$/) do |command| @success, @output = TestApp.run(command) end When(/^I rollback to a specific release$/) do @rollback_release = @release_paths.first.split("/").last step %Q{I run cap "deploy:rollback ROLLBACK_RELEASE=#{@rollback_release}"} end capistrano-3.10.0/features/configuration.feature0000644000004100000410000000200713175754131022036 0ustar www-datawww-dataFeature: The path to the configuration can be changed, removing the need to follow Ruby/Rails conventions Background: Given a test app with the default configuration And servers with the roles app and web Scenario: Deploying with configuration in default location When I run "cap test" Then the task is successful Scenario: Deploying with configuration in a custom location But the configuration is in a custom location When I run "cap test" Then the task is successful Scenario: Show install task with configuration in default location When I run "cap -T" Then the task is successful And contains "install" in the output Scenario: Hide install task with configuration in a custom location And config stage file has line "desc 'Special Task'" And config stage file has line "task :special_stage_task" But the configuration is in a custom location When I run "cap -T" Then the task is successful And doesn't contain "special_stage_task" in the output capistrano-3.10.0/features/deploy.feature0000644000004100000410000000557513175754131020500 0ustar www-datawww-dataFeature: Deploy Background: Given a test app with the default configuration And servers with the roles app and web Scenario: Creating the repo When I run cap "git:check" Then the task is successful And git wrapper permissions are 0700 Scenario: Creating the directory structure When I run cap "deploy:check:directories" Then the shared path is created And the releases path is created Scenario: Creating linked directories When I run cap "deploy:check:linked_dirs" Then directories in :linked_dirs are created in shared Scenario: Creating linked directories for linked files When I run cap "deploy:check:make_linked_dirs" Then directories referenced in :linked_files are created in shared Scenario: Checking linked files - missing file Given a linked file "missing_file.txt" But file "missing_file.txt" does not exist in shared path When I run cap "deploy:check:linked_files" Then the task fails Scenario: Checking linked files - file exists Given a linked file "existing_file.txt" And file "existing_file.txt" exists in shared path When I run cap "deploy:check:linked_files" Then the task is successful Scenario: Creating a release Given I run cap "deploy:check:directories" When I run cap "git:create_release" as part of a release Then the repo is cloned And the release is created Scenario: Symlink linked files When I run cap "deploy:symlink:linked_files deploy:symlink:release" as part of a release Then file symlinks are created in the new release Scenario: Symlink linked dirs When I run cap "deploy:symlink:linked_dirs" as part of a release Then directory symlinks are created in the new release Scenario: Publishing When I run cap "deploy:symlink:release" Then the current directory will be a symlink to the release Scenario: Cleanup Given config stage file has line "set :keep_releases, 3" And 5 valid existing releases And an invalid release named "new" When I run cap "deploy:cleanup" Then 3 valid releases are kept And the invalid "new" release is ignored Scenario: Cleanup after many failed releases doesn't remove last good release Given config stage file has line "set :keep_releases, 2" And I make 2 deployments And an invalid release named "77777777777777" And an invalid release named "88888888888888" And an invalid release named "99999999999999" When I run cap "deploy:cleanup" Then 3 valid releases are kept And the current directory will be a symlink to the release Scenario: Rolling Back Given I make 2 deployments When I run cap "deploy:rollback" Then the current symlink points to the previous release Scenario: Rolling Back to a specific release Given I make 3 deployments When I rollback to a specific release Then the current symlink points to that specific release capistrano-3.10.0/features/stage_failure.feature0000644000004100000410000000031713175754131022003 0ustar www-datawww-dataFeature: Stage failure Background: Given a test app with the default configuration And a stage file named deploy.rb Scenario: Running a task When I run cap "doctor" Then the task fails capistrano-3.10.0/features/deploy_failure.feature0000644000004100000410000000100113175754131022163 0ustar www-datawww-dataFeature: Deploy failure Background: Given a test app with the default configuration And a custom task that will simulate a failure And a custom task to run in the event of a failure And servers with the roles app and web Scenario: Triggering the custom task When I run cap "deploy:starting" But an error is raised Then the failure task will not run Scenario: Triggering the custom task When I run cap "deploy" But an error is raised Then the failure task will run capistrano-3.10.0/features/sshconnect.feature0000644000004100000410000000060113175754131021334 0ustar www-datawww-dataFeature: SSH Connection Background: Given a test app with the default configuration And servers with the roles app and web And a task which executes as root Scenario: Switching from default user to root and back again When I run cap "am_i_root" Then the task is successful And the output matches "I am uid=0\(root\)" followed by "I am uid=\d+\(vagrant\)" capistrano-3.10.0/features/installation.feature0000644000004100000410000000117013175754131021670 0ustar www-datawww-dataFeature: Installation Background: Given a test app without any configuration Scenario: The "install" task documentation can be viewed When I run "cap -T" Then the task is successful And contains "cap install" in the output Scenario: With default stages When I run "cap install" Then the deploy.rb file is created And the default stage files are created And the tasks folder is created Scenario: With specified stages When I run "cap install STAGES=qa,production" Then the deploy.rb file is created And the specified stage files are created And the tasks folder is created capistrano-3.10.0/features/subdirectory.feature0000644000004100000410000000050513175754131021706 0ustar www-datawww-dataFeature: cap can be run from a subdirectory, and will still find the Capfile Background: Given a test app with the default configuration And servers with the roles app and web Scenario: Running cap from a subdirectory When I run cap "git:check" within the "config" directory Then the task is successful capistrano-3.10.0/features/doctor.feature0000644000004100000410000000046213175754131020464 0ustar www-datawww-dataFeature: Doctor Background: Given a test app with the default configuration Scenario: Running the doctor task When I run cap "doctor" Then the task is successful And contains "Environment" in the output And contains "Gems" in the output And contains "Variables" in the output capistrano-3.10.0/features/support/0000755000004100000410000000000013175754131017327 5ustar www-datawww-datacapistrano-3.10.0/features/support/remote_command_helpers.rb0000644000004100000410000000102513175754131024365 0ustar www-datawww-datamodule RemoteCommandHelpers def test_dir_exists(path) exists?("d", path) end def test_symlink_exists(path) exists?("L", path) end def test_file_exists(path) exists?("f", path) end def exists?(type, path) %Q{[ -#{type} "#{path}" ]} end def symlinked?(symlink_path, target_path) "[ #{symlink_path} -ef #{target_path} ]" end def safely_remove_file(_path) run_vagrant_command("rm #{test_file}") rescue VagrantHelpers::VagrantSSHCommandError end end World(RemoteCommandHelpers) capistrano-3.10.0/features/support/vagrant_helpers.rb0000644000004100000410000000153713175754131023046 0ustar www-datawww-datarequire "open3" module VagrantHelpers extend self class VagrantSSHCommandError < RuntimeError; end at_exit do if ENV["KEEP_RUNNING"] puts "Vagrant vm will be left up because KEEP_RUNNING is set." puts "Rerun without KEEP_RUNNING set to cleanup the vm." else vagrant_cli_command("destroy -f") end end def vagrant_cli_command(command) puts "[vagrant] #{command}" stdout, stderr, status = Dir.chdir(VAGRANT_ROOT) do Open3.capture3("#{VAGRANT_BIN} #{command}") end (stdout + stderr).each_line { |line| puts "[vagrant] #{line}" } [stdout, stderr, status] end def run_vagrant_command(command) stdout, stderr, status = vagrant_cli_command("ssh -c #{command.inspect}") return [stdout, stderr] if status.success? raise VagrantSSHCommandError, status end end World(VagrantHelpers) capistrano-3.10.0/features/support/env.rb0000644000004100000410000000047613175754131020453 0ustar www-datawww-dataPROJECT_ROOT = File.expand_path("../../../", __FILE__) VAGRANT_ROOT = File.join(PROJECT_ROOT, "spec/support") VAGRANT_BIN = ENV["VAGRANT_BIN"] || "vagrant" at_exit do if ENV["KEEP_RUNNING"] VagrantHelpers.run_vagrant_command("rm -rf /home/vagrant/var") end end require_relative "../../spec/support/test_app" capistrano-3.10.0/UPGRADING-3.7.md0000644000004100000410000000501713175754131016147 0ustar www-datawww-data# Capistrano 3.7.0 upgrade guide ## The :scm variable is deprecated Up until now, Capistrano's SCM was configured using the `:scm` variable: ```ruby # This is now deprecated set :scm, :svn ``` To avoid deprecation warnings: 1. Remove `set :scm, ...` from your Capistrano configuration. 2. Add *one* of the following SCM declarations to your `Capfile` after `require "capistrano/deploy"`: ```ruby # To use Git require "capistrano/scm/git" install_plugin Capistrano::SCM::Git # To use Mercurial require "capistrano/scm/hg" install_plugin Capistrano::SCM::Hg # To use Subversion require "capistrano/scm/svn" install_plugin Capistrano::SCM::Svn ``` ## This is the last release where Git is the automatic default If you do not specify an SCM, Capistrano assumes Git. However this behavior is now deprecated. Add this to your Capfile to avoid deprecation warnings: ```ruby require "capistrano/scm/git" install_plugin Capistrano::SCM::Git ``` ## :git_strategy, :hg_strategy, and :svn_strategy are removed Capistrano 3.7.0 has a rewritten SCM system that relies on "plugins". This system is more flexible than the old "strategy" system that only allowed certain parts of SCM tasks to be customized. If your deployment relies on a custom SCM strategy, you will need to rewrite that strategy to be a full-fledged SCM plugin instead. There is a fairly straightforward migration path: write your plugin to be a subclass of the built-in SCM that you want to customize. For example: ```ruby require "capistrano/scm/git" class MyCustomGit < Capistrano::SCM::Git # Override the methods you wish to customize, e.g.: def clone_repo # ... end end ``` Then use your plugin in by loading it in the Capfile: ```ruby require_relative "path/to/my_custom_git.rb" install_plugin MyCustomGit ``` ## Existing third-party SCMs are deprecated If you are using a third-party SCM, you can continue using it without changes, but you will see deprecation warnings. Contact the maintainer of the third-party SCM gem and ask them about modifying the gem to work with the new Capistrano 3.7.0 SCM plugin system. ## remote_file is removed The `remote_file` method is no longer in Capistrano 3.7.0. You can read the discussion that led to its removal here: [issue 762](https://github.com/capistrano/capistrano/issues/762). There is no direct replacement. To migrate to 3.7.0, you will need to rewrite any parts of your deployment that use `remote_file` to use a different mechanism for uploading files. Consider using the `upload!` method directly in a procedural fashion instead. capistrano-3.10.0/Dangerfile0000644000004100000410000000006613175754131015762 0ustar www-datawww-datadanger.import_dangerfile(github: "capistrano/danger") capistrano-3.10.0/LICENSE.txt0000644000004100000410000000210513175754131015616 0ustar www-datawww-dataMIT License (MIT) Copyright (c) 2012-2016 Tom Clements, Lee Hambley Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. capistrano-3.10.0/spec/0000755000004100000410000000000013175754131014727 5ustar www-datawww-datacapistrano-3.10.0/spec/spec_helper.rb0000644000004100000410000000076113175754131017551 0ustar www-datawww-data$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) $LOAD_PATH.unshift(File.dirname(__FILE__)) require "capistrano/all" require "rspec" require "mocha/api" require "time" # Requires supporting files with custom matchers and macros, etc, # in ./support/ and its subdirectories. Dir['#{File.dirname(__FILE__)}/support/**/*.rb'].each { |f| require f } RSpec.configure do |config| config.raise_errors_for_deprecations! config.mock_framework = :mocha config.order = "random" end capistrano-3.10.0/spec/lib/0000755000004100000410000000000013175754131015475 5ustar www-datawww-datacapistrano-3.10.0/spec/lib/capistrano_spec.rb0000644000004100000410000000015713175754131021202 0ustar www-datawww-datarequire "spec_helper" module Capistrano describe Application do let(:app) { Application.new } end end capistrano-3.10.0/spec/lib/capistrano/0000755000004100000410000000000013175754131017640 5ustar www-datawww-datacapistrano-3.10.0/spec/lib/capistrano/scm/0000755000004100000410000000000013175754131020422 5ustar www-datawww-datacapistrano-3.10.0/spec/lib/capistrano/scm/git_spec.rb0000644000004100000410000001220313175754131022542 0ustar www-datawww-datarequire "spec_helper" require "capistrano/scm/git" module Capistrano describe SCM::Git do subject { Capistrano::SCM::Git.new } # This allows us to easily use `set`, `fetch`, etc. in the examples. let(:env) { Capistrano::Configuration.env } # Stub the SSHKit backend so we can set up expectations without the plugin # actually executing any commands. let(:backend) { stub } before { SSHKit::Backend.stubs(:current).returns(backend) } # Mimic the deploy flow tasks so that the plugin can register its hooks. before do Rake::Task.define_task("deploy:new_release_path") Rake::Task.define_task("deploy:check") Rake::Task.define_task("deploy:set_current_revision") end # Clean up any tasks or variables that the plugin defined. after do Rake::Task.clear Capistrano::Configuration.reset! end describe "#set_defaults" do it "makes git_wrapper_path using application, stage, and local_user" do env.set(:tmp_dir, "/tmp") env.set(:application, "my_app") env.set(:stage, "staging") env.set(:local_user, "(Git Web User) via ShipIt") subject.set_defaults expect(env.fetch(:git_wrapper_path)).to eq("/tmp/git-ssh-my_app-staging-(Git Web User) via ShipIt.sh") end end describe "#git" do it "should call execute git in the context, with arguments" do backend.expects(:execute).with(:git, :init) subject.git(:init) end end describe "#repo_mirror_exists?" do it "should call test for repo HEAD" do env.set(:repo_path, "/path/to/repo") backend.expects(:test).with " [ -f /path/to/repo/HEAD ] " subject.repo_mirror_exists? end end describe "#check_repo_is_reachable" do it "should test the repo url" do env.set(:repo_url, "url") backend.expects(:execute).with(:git, :'ls-remote', "url", "HEAD").returns(true) subject.check_repo_is_reachable end end describe "#clone_repo" do it "should run git clone" do env.set(:repo_url, "url") env.set(:repo_path, "path") backend.expects(:execute).with(:git, :clone, "--mirror", "url", "path") subject.clone_repo end it "should run git clone in shallow mode" do env.set(:git_shallow_clone, "1") env.set(:repo_url, "url") env.set(:repo_path, "path") backend.expects(:execute).with(:git, :clone, "--mirror", "--depth", "1", "--no-single-branch", "url", "path") subject.clone_repo end context "with username and password specified" do before do env.set(:git_http_username, "hello") env.set(:git_http_password, "topsecret") env.set(:repo_url, "https://example.com/repo.git") env.set(:repo_path, "path") end it "should include the credentials in the url" do backend.expects(:execute).with(:git, :clone, "--mirror", "https://hello:topsecret@example.com/repo.git", "path") subject.clone_repo end end end describe "#update_mirror" do it "should run git update" do env.set(:repo_url, "url") backend.expects(:execute).with(:git, :remote, "set-url", "origin", "url") backend.expects(:execute).with(:git, :remote, :update, "--prune") subject.update_mirror end it "should run git update in shallow mode" do env.set(:git_shallow_clone, "1") env.set(:branch, "branch") env.set(:repo_url, "url") backend.expects(:execute).with(:git, :remote, "set-url", "origin", "url") backend.expects(:execute).with(:git, :fetch, "--depth", "1", "origin", "branch") subject.update_mirror end end describe "#archive_to_release_path" do it "should run git archive without a subtree" do env.set(:branch, "branch") env.set(:release_path, "path") backend.expects(:execute).with(:git, :archive, "branch", "| /usr/bin/env tar -x -f - -C", "path") subject.archive_to_release_path end it "should run git archive with a subtree" do env.set(:repo_tree, "tree") env.set(:branch, "branch") env.set(:release_path, "path") backend.expects(:execute).with(:git, :archive, "branch", "tree", "| /usr/bin/env tar -x --strip-components 1 -f - -C", "path") subject.archive_to_release_path end it "should run tar with an overridden name" do env.set(:branch, "branch") env.set(:release_path, "path") SSHKit.config.command_map.expects(:[]).with(:tar).returns("/usr/bin/env gtar") backend.expects(:execute).with(:git, :archive, "branch", "| /usr/bin/env gtar -x -f - -C", "path") subject.archive_to_release_path end end describe "#fetch_revision" do it "should capture git rev-list" do env.set(:branch, "branch") backend.expects(:capture).with(:git, "rev-list --max-count=1 branch").returns("81cec13b777ff46348693d327fc8e7832f79bf43") revision = subject.fetch_revision expect(revision).to eq("81cec13b777ff46348693d327fc8e7832f79bf43") end end end end capistrano-3.10.0/spec/lib/capistrano/scm/hg_spec.rb0000644000004100000410000000626613175754131022371 0ustar www-datawww-datarequire "spec_helper" require "capistrano/scm/hg" module Capistrano describe SCM::Hg do subject { Capistrano::SCM::Hg.new } # This allows us to easily use `set`, `fetch`, etc. in the examples. let(:env) { Capistrano::Configuration.env } # Stub the SSHKit backend so we can set up expectations without the plugin # actually executing any commands. let(:backend) { stub } before { SSHKit::Backend.stubs(:current).returns(backend) } # Mimic the deploy flow tasks so that the plugin can register its hooks. before do Rake::Task.define_task("deploy:new_release_path") Rake::Task.define_task("deploy:check") Rake::Task.define_task("deploy:set_current_revision") end # Clean up any tasks or variables that the plugin defined. after do Rake::Task.clear Capistrano::Configuration.reset! end describe "#hg" do it "should call execute hg in the context, with arguments" do backend.expects(:execute).with(:hg, :init) subject.hg(:init) end end describe "#repo_mirror_exists?" do it "should call test for repo HEAD" do env.set(:repo_path, "/path/to/repo") backend.expects(:test).with " [ -d /path/to/repo/.hg ] " subject.repo_mirror_exists? end end describe "#check_repo_is_reachable" do it "should test the repo url" do env.set(:repo_url, :url) backend.expects(:execute).with(:hg, "id", :url) subject.check_repo_is_reachable end end describe "#clone_repo" do it "should run hg clone" do env.set(:repo_url, :url) env.set(:repo_path, "path") backend.expects(:execute).with(:hg, "clone", "--noupdate", :url, "path") subject.clone_repo end end describe "#update_mirror" do it "should run hg update" do backend.expects(:execute).with(:hg, "pull") subject.update_mirror end end describe "#archive_to_release_path" do it "should run hg archive without a subtree" do env.set(:branch, :branch) env.set(:release_path, "path") backend.expects(:execute).with(:hg, "archive", "path", "--rev", :branch) subject.archive_to_release_path end it "should run hg archive with a subtree" do env.set(:repo_tree, "tree") env.set(:branch, :branch) env.set(:release_path, "path") env.set(:tmp_dir, "/tmp") SecureRandom.stubs(:hex).with(10).returns("random") backend.expects(:execute).with(:hg, "archive -p . -I", "tree", "--rev", :branch, "/tmp/random.tar") backend.expects(:execute).with(:mkdir, "-p", "path") backend.expects(:execute).with(:tar, "-x --strip-components 1 -f", "/tmp/random.tar", "-C", "path") backend.expects(:execute).with(:rm, "/tmp/random.tar") subject.archive_to_release_path end end describe "#fetch_revision" do it "should capture hg log" do env.set(:branch, :branch) backend.expects(:capture).with(:hg, "log --rev branch --template \"{node}\n\"").returns("01abcde") revision = subject.fetch_revision expect(revision).to eq("01abcde") end end end end capistrano-3.10.0/spec/lib/capistrano/scm/svn_spec.rb0000644000004100000410000001064613175754131022576 0ustar www-datawww-datarequire "spec_helper" require "capistrano/scm/svn" module Capistrano describe SCM::Svn do subject { Capistrano::SCM::Svn.new } # This allows us to easily use `set`, `fetch`, etc. in the examples. let(:env) { Capistrano::Configuration.env } # Stub the SSHKit backend so we can set up expectations without the plugin # actually executing any commands. let(:backend) { stub } before { SSHKit::Backend.stubs(:current).returns(backend) } # Mimic the deploy flow tasks so that the plugin can register its hooks. before do Rake::Task.define_task("deploy:new_release_path") Rake::Task.define_task("deploy:check") Rake::Task.define_task("deploy:set_current_revision") end # Clean up any tasks or variables that the plugin defined. after do Rake::Task.clear Capistrano::Configuration.reset! end describe "#svn" do it "should call execute svn in the context, with arguments" do env.set(:svn_username, "someuser") env.set(:svn_password, "somepassword") backend.expects(:execute).with(:svn, :init, "--username someuser", "--password somepassword") subject.svn(:init) end end describe "#repo_mirror_exists?" do it "should call test for repo HEAD" do env.set(:repo_path, "/path/to/repo") backend.expects(:test).with " [ -d /path/to/repo/.svn ] " subject.repo_mirror_exists? end end describe "#check_repo_is_reachable" do it "should test the repo url" do env.set(:repo_url, :url) env.set(:svn_username, "someuser") env.set(:svn_password, "somepassword") backend.expects(:test).with(:svn, :info, :url, "--username someuser", "--password somepassword").returns(true) subject.check_repo_is_reachable end end describe "#clone_repo" do it "should run svn checkout" do env.set(:repo_url, :url) env.set(:repo_path, "path") env.set(:svn_username, "someuser") env.set(:svn_password, "somepassword") backend.expects(:execute).with(:svn, :checkout, :url, "path", "--username someuser", "--password somepassword") subject.clone_repo end end describe "#update_mirror" do it "should run svn update" do env.set(:repo_url, "url") env.set(:repo_path, "path") backend.expects(:capture).with(:svn, :info, "path").returns("URL: url\n") env.set(:svn_username, "someuser") env.set(:svn_password, "somepassword") backend.expects(:execute).with(:svn, :update, "--username someuser", "--password somepassword") subject.update_mirror end context "for specific revision" do it "should run svn update" do env.set(:repo_url, "url") env.set(:repo_path, "path") backend.expects(:capture).with(:svn, :info, "path").returns("URL: url\n") env.set(:svn_username, "someuser") env.set(:svn_password, "somepassword") env.set(:svn_revision, "12345") backend.expects(:execute).with(:svn, :update, "--username someuser", "--password somepassword", "--revision 12345") subject.update_mirror end end it "should run svn switch if repo_url is changed" do env.set(:repo_url, "url") env.set(:repo_path, "path") backend.expects(:capture).with(:svn, :info, "path").returns("URL: old_url\n") env.set(:svn_username, "someuser") env.set(:svn_password, "somepassword") backend.expects(:execute).with(:svn, :switch, "url", "--username someuser", "--password somepassword") backend.expects(:execute).with(:svn, :update, "--username someuser", "--password somepassword") subject.update_mirror end end describe "#archive_to_release_path" do it "should run svn export" do env.set(:release_path, "path") env.set(:svn_username, "someuser") env.set(:svn_password, "somepassword") backend.expects(:execute).with(:svn, :export, "--force", ".", "path", "--username someuser", "--password somepassword") subject.archive_to_release_path end end describe "#fetch_revision" do it "should capture svn version" do env.set(:repo_path, "path") backend.expects(:capture).with(:svnversion, "path").returns("12345") revision = subject.fetch_revision expect(revision).to eq("12345") end end end end capistrano-3.10.0/spec/lib/capistrano/immutable_task_spec.rb0000644000004100000410000000144513175754131024204 0ustar www-datawww-datarequire "spec_helper" require "rake" require "capistrano/immutable_task" module Capistrano describe ImmutableTask do after do # Ensure that any tasks we create in these tests don't pollute other tests Rake::Task.clear end it "prints warning and raises when task is enhanced" do extend(Rake::DSL) load_defaults = Rake::Task.define_task("load:defaults") load_defaults.extend(Capistrano::ImmutableTask) $stderr.expects(:puts).with do |message| message =~ /^ERROR: load:defaults has already been invoked/ end expect do namespace :load do task :defaults do # Never reached since load_defaults is frozen and can't be enhanced end end end.to raise_error(/frozen/i) end end end capistrano-3.10.0/spec/lib/capistrano/scm_spec.rb0000644000004100000410000000455113175754131021766 0ustar www-datawww-datarequire "spec_helper" require "capistrano/scm" module RaiseNotImplementedMacro def raise_not_implemented_on(method) it "should raise NotImplemented on #{method}" do expect do subject.send(method) end.to raise_error(NotImplementedError) end end end RSpec.configure do include RaiseNotImplementedMacro end module DummyStrategy def test test!("you dummy!") end end module BlindStrategy; end module Capistrano describe SCM do let(:context) { mock } describe "#initialize" do subject { Capistrano::SCM.new(context, DummyStrategy) } it "should load the provided strategy" do context.expects(:test).with("you dummy!") subject.test end end describe "Convenience methods" do subject { Capistrano::SCM.new(context, BlindStrategy) } describe "#test!" do it "should return call test on the context" do context.expects(:test).with(:x) subject.test!(:x) end end describe "#repo_url" do it "should return the repo url according to the context" do context.expects(:repo_url).returns(:url) expect(subject.repo_url).to eq(:url) end end describe "#repo_path" do it "should return the repo path according to the context" do context.expects(:repo_path).returns(:path) expect(subject.repo_path).to eq(:path) end end describe "#release_path" do it "should return the release path according to the context" do context.expects(:release_path).returns("/path/to/nowhere") expect(subject.release_path).to eq("/path/to/nowhere") end end describe "#fetch" do it "should call fetch on the context" do context.expects(:fetch) subject.fetch(:branch) end end end describe "With a 'blind' strategy" do subject { Capistrano::SCM.new(context, BlindStrategy) } describe "#test" do raise_not_implemented_on(:test) end describe "#check" do raise_not_implemented_on(:check) end describe "#clone" do raise_not_implemented_on(:clone) end describe "#update" do raise_not_implemented_on(:update) end describe "#release" do raise_not_implemented_on(:release) end end end end capistrano-3.10.0/spec/lib/capistrano/version_validator_spec.rb0000644000004100000410000000547213175754131024741 0ustar www-datawww-datarequire "spec_helper" module Capistrano describe VersionValidator do let(:validator) { VersionValidator.new(version) } let(:version) { stub } describe "#new" do it "takes a version" do expect(validator) end end describe "#verify" do let(:current_version) { "3.0.1" } subject { validator.verify } before do validator.stubs(:current_version).returns(current_version) end context "with exact version" do context "valid" do let(:version) { "3.0.1" } it { expect(subject).to be_truthy } end context "invalid - lower" do let(:version) { "3.0.0" } it "fails" do expect { subject }.to raise_error(RuntimeError) end end context "invalid - higher" do let(:version) { "3.0.2" } it "fails" do expect { subject }.to raise_error(RuntimeError) end end end context "with optimistic versioning" do context "valid" do let(:version) { ">= 3.0.0" } it { expect(subject).to be_truthy } end context "invalid - lower" do let(:version) { "<= 2.0.0" } it "fails" do expect { subject }.to raise_error(RuntimeError) end end end context "with pessimistic versioning" do context "2 decimal places" do context "valid" do let(:version) { "~> 3.0.0" } it { expect(subject).to be_truthy } end context "invalid" do let(:version) { "~> 3.1.0" } it "fails" do expect { subject }.to raise_error(RuntimeError) end end end context "1 decimal place" do let(:current_version) { "3.5.0" } context "valid" do let(:version) { "~> 3.1" } it { expect(subject).to be_truthy } end context "invalid" do let(:version) { "~> 3.6" } it "fails" do expect { subject }.to raise_error(RuntimeError) end end end context "with multiple versions" do let(:current_version) { "3.5.9" } context "valid" do let(:version) { [">= 3.5.0", "< 3.5.10"] } it { is_expected.to be_truthy } end context "invalid" do let(:version) { [">= 3.5.0", "< 3.5.8"] } it "fails" do expect { subject }.to raise_error(RuntimeError) end end context "invalid" do let(:version) { ["> 3.5.9", "< 3.5.13"] } it "fails" do expect { subject }.to raise_error(RuntimeError) end end end end end end end capistrano-3.10.0/spec/lib/capistrano/configuration/0000755000004100000410000000000013175754131022507 5ustar www-datawww-datacapistrano-3.10.0/spec/lib/capistrano/configuration/server_spec.rb0000644000004100000410000002137313175754131025362 0ustar www-datawww-datarequire "spec_helper" module Capistrano class Configuration describe Server do let(:server) { Server.new("root@hostname:1234") } describe "adding a role" do subject { server.add_role(:test) } it "adds the role" do expect { subject }.to change { server.roles.size }.from(0).to(1) end end describe "adding roles" do subject { server.add_roles(%i(things stuff)) } it "adds the roles" do expect { subject }.to change { server.roles.size }.from(0).to(2) end end describe "checking roles" do subject { server.has_role?(:test) } before do server.add_role(:test) end it "adds the role" do expect(subject).to be_truthy end end describe "comparing identity" do subject { server.hostname == Server[hostname].hostname } context "with the same user, hostname and port" do let(:hostname) { "root@hostname:1234" } it { expect(subject).to be_truthy } end context "with a different user" do let(:hostname) { "deployer@hostname:1234" } it { expect(subject).to be_truthy } end context "with a different port" do let(:hostname) { "root@hostname:5678" } it { expect(subject).to be_truthy } end context "with a different hostname" do let(:hostname) { "root@otherserver:1234" } it { expect(subject).to be_falsey } end end describe "identifying as primary" do subject { server.primary } context "server is primary" do before do server.set(:primary, true) end it "returns self" do expect(subject).to eq server end end context "server is not primary" do it "is falesy" do expect(subject).to be_falsey end end end describe "assigning properties" do before do server.with(properties) end context "properties contains roles" do let(:properties) { { roles: [:clouds] } } it "adds the roles" do expect(server.roles.first).to eq :clouds end end context "properties contains user" do let(:properties) { { user: "tomc" } } it "sets the user" do expect(server.user).to eq "tomc" end it "sets the netssh_options user" do expect(server.netssh_options[:user]).to eq "tomc" end end context "properties contains port" do let(:properties) { { port: 2222 } } it "sets the port" do expect(server.port).to eq 2222 end end context "properties contains key" do let(:properties) { { key: "/key" } } it "adds the key" do expect(server.keys).to include "/key" end end context "properties contains password" do let(:properties) { { password: "supersecret" } } it "adds the key" do expect(server.password).to eq "supersecret" end end context "new properties" do let(:properties) { { webscales: 5 } } it "adds the properties" do expect(server.properties.webscales).to eq 5 end end context "existing properties" do let(:properties) { { webscales: 6 } } it "keeps the existing properties" do expect(server.properties.webscales).to eq 6 server.properties.webscales = 5 expect(server.properties.webscales).to eq 5 end end end describe "#include?" do let(:options) { {} } subject { server.select?(options) } before do server.properties.active = true end context "options are empty" do it { expect(subject).to be_truthy } end context "value is a symbol" do context "value matches server property" do context "with :filter" do let(:options) { { filter: :active } } it { expect(subject).to be_truthy } end context "with :select" do let(:options) { { select: :active } } it { expect(subject).to be_truthy } end context "with :exclude" do let(:options) { { exclude: :active } } it { expect(subject).to be_falsey } end end context "value does not match server properly" do context "with :active true" do let(:options) { { active: true } } it { expect(subject).to be_truthy } end context "with :active false" do let(:options) { { active: false } } it { expect(subject).to be_falsey } end end context "value does not match server properly" do context "with :filter" do let(:options) { { filter: :inactive } } it { expect(subject).to be_falsey } end context "with :select" do let(:options) { { select: :inactive } } it { expect(subject).to be_falsey } end context "with :exclude" do let(:options) { { exclude: :inactive } } it { expect(subject).to be_truthy } end end end context "key is a property" do context "with :active true" do let(:options) { { active: true } } it { expect(subject).to be_truthy } end context "with :active false" do let(:options) { { active: false } } it { expect(subject).to be_falsey } end end context "value is a proc" do context "value matches server property" do context "with :filter" do let(:options) { { filter: ->(s) { s.properties.active } } } it { expect(subject).to be_truthy } end context "with :select" do let(:options) { { select: ->(s) { s.properties.active } } } it { expect(subject).to be_truthy } end context "with :exclude" do let(:options) { { exclude: ->(s) { s.properties.active } } } it { expect(subject).to be_falsey } end end context "value does not match server properly" do context "with :filter" do let(:options) { { filter: ->(s) { s.properties.inactive } } } it { expect(subject).to be_falsey } end context "with :select" do let(:options) { { select: ->(s) { s.properties.inactive } } } it { expect(subject).to be_falsey } end context "with :exclude" do let(:options) { { exclude: ->(s) { s.properties.inactive } } } it { expect(subject).to be_truthy } end end end end describe "assign ssh_options" do let(:server) { Server.new("user_name@hostname") } context "defaults" do it "forward agent" do expect(server.netssh_options[:forward_agent]).to eq true end it "contains user" do expect(server.netssh_options[:user]).to eq "user_name" end end context "custom" do let(:properties) do { ssh_options: { user: "another_user", keys: %w(/home/another_user/.ssh/id_rsa), forward_agent: false, auth_methods: %w(publickey password) } } end before do server.with(properties) end it "not forward agent" do expect(server.netssh_options[:forward_agent]).to eq false end it "contains correct user" do expect(server.netssh_options[:user]).to eq "another_user" end it "does not affect server user in host" do expect(server.user).to eq "user_name" end it "contains keys" do expect(server.netssh_options[:keys]).to eq %w(/home/another_user/.ssh/id_rsa) end it "contains auth_methods" do expect(server.netssh_options[:auth_methods]).to eq %w(publickey password) end end end describe ".[]" do it "creates a server if its argument is not already a server" do expect(Server["hostname:1234"]).to be_a Server end it "returns its argument if it is already a server" do expect(Server[server]).to be server end end end end end capistrano-3.10.0/spec/lib/capistrano/configuration/null_filter_spec.rb0000644000004100000410000000056113175754131026367 0ustar www-datawww-datarequire "spec_helper" module Capistrano class Configuration describe NullFilter do subject(:null_filter) { NullFilter.new } describe "#filter" do let(:servers) { mock("servers") } it "returns the servers passed in as arguments" do expect(null_filter.filter(servers)).to eq(servers) end end end end end capistrano-3.10.0/spec/lib/capistrano/configuration/servers_spec.rb0000644000004100000410000003022213175754131025536 0ustar www-datawww-datarequire "spec_helper" module Capistrano class Configuration describe Servers do let(:servers) { Servers.new } describe "adding a role" do it "adds two new server instances" do expect { servers.add_role(:app, %w{1 2}) } .to change { servers.count }.from(0).to(2) end it "handles de-duplification within roles" do servers.add_role(:app, %w{1}) servers.add_role(:app, %w{1}) expect(servers.count).to eq 1 end it "handles de-duplification within roles with users" do servers.add_role(:app, %w{1}, user: "nick") servers.add_role(:app, %w{1}, user: "fred") expect(servers.count).to eq 1 end it "accepts instances of server objects" do servers.add_role(:app, [Capistrano::Configuration::Server.new("example.net"), "example.com"]) expect(servers.roles_for([:app]).length).to eq 2 end it "accepts non-enumerable types" do servers.add_role(:app, "1") expect(servers.roles_for([:app]).count).to eq 1 end it "creates distinct server properties" do servers.add_role(:db, %w{1 2}, db: { port: 1234 }) servers.add_host("1", db: { master: true }) expect(servers.count).to eq(2) expect(servers.roles_for([:db]).count).to eq 2 expect(servers.find { |s| s.hostname == "1" }.properties.db).to eq(port: 1234, master: true) expect(servers.find { |s| s.hostname == "2" }.properties.db).to eq(port: 1234) end end describe "adding a role to an existing server" do before do servers.add_role(:web, %w{1 2}) servers.add_role(:app, %w{1 2}) end it "adds new roles to existing servers" do expect(servers.count).to eq 2 end end describe "collecting server roles" do let(:app) { Set.new([:app]) } let(:web_app) { Set.new(%i(web app)) } let(:web) { Set.new([:web]) } before do servers.add_role(:app, %w{1 2 3}) servers.add_role(:web, %w{2 3 4}) end it "returns an array of the roles" do expect(servers.roles_for([:app]).collect(&:roles)).to eq [app, web_app, web_app] expect(servers.roles_for([:web]).collect(&:roles)).to eq [web_app, web_app, web] end end describe "finding the primary server" do after do Configuration.reset! end it "takes the first server if none have the primary property" do servers.add_role(:app, %w{1 2}) expect(servers.fetch_primary(:app).hostname).to eq("1") end it "takes the first server with the primary have the primary flag" do servers.add_role(:app, %w{1 2}) servers.add_host("2", primary: true) expect(servers.fetch_primary(:app).hostname).to eq("2") end it "ignores any on_filters" do Configuration.env.set :filter, host: "1" servers.add_role(:app, %w{1 2}) servers.add_host("2", primary: true) expect(servers.fetch_primary(:app).hostname).to eq("2") end end describe "fetching servers" do before do servers.add_role(:app, %w{1 2}) servers.add_role(:web, %w{2 3}) end it "returns the correct app servers" do expect(servers.roles_for([:app]).map(&:hostname)).to eq %w{1 2} end it "returns the correct web servers" do expect(servers.roles_for([:web]).map(&:hostname)).to eq %w{2 3} end it "returns the correct app and web servers" do expect(servers.roles_for(%i(app web)).map(&:hostname)).to eq %w{1 2 3} end it "returns all servers" do expect(servers.roles_for([:all]).map(&:hostname)).to eq %w{1 2 3} end end describe "adding a server" do before do servers.add_host("1", roles: [:app, "web"], test: :value) end it "can create a server with properties" do expect(servers.roles_for([:app]).first.hostname).to eq "1" expect(servers.roles_for([:web]).first.hostname).to eq "1" expect(servers.roles_for([:all]).first.properties.test).to eq :value expect(servers.roles_for([:all]).first.properties.keys).to eq [:test] end it "can accept multiple servers with the same hostname but different ports or users" do servers.add_host("1", roles: [:app, "web"], test: :value, port: 12) expect(servers.count).to eq(2) servers.add_host("1", roles: [:app, "web"], test: :value, port: 34) servers.add_host("1", roles: [:app, "web"], test: :value, user: "root") servers.add_host("1", roles: [:app, "web"], test: :value, user: "deployer") servers.add_host("1", roles: [:app, "web"], test: :value, user: "root", port: 34) servers.add_host("1", roles: [:app, "web"], test: :value, user: "deployer", port: 34) servers.add_host("1", roles: [:app, "web"], test: :value, user: "deployer", port: 56) expect(servers.count).to eq(4) end describe "with a :user property" do it "sets the server ssh username" do servers.add_host("1", roles: [:app, "web"], user: "nick") expect(servers.count).to eq(1) expect(servers.roles_for([:all]).first.user).to eq "nick" end it "overwrites the value of a user specified in the hostname" do servers.add_host("brian@1", roles: [:app, "web"], user: "nick") expect(servers.count).to eq(1) expect(servers.roles_for([:all]).first.user).to eq "nick" end end it "overwrites the value of a previously defined scalar property" do servers.add_host("1", roles: [:app, "web"], test: :volatile) expect(servers.count).to eq(1) expect(servers.roles_for([:all]).first.properties.test).to eq :volatile end it "merges previously defined hash properties" do servers.add_host("1", roles: [:b], db: { port: 1234 }) servers.add_host("1", roles: [:b], db: { master: true }) expect(servers.count).to eq(1) expect(servers.roles_for([:b]).first.properties.db).to eq(port: 1234, master: true) end it "concatenates previously defined array properties" do servers.add_host("1", roles: [:b], steps: [1, 3, 5]) servers.add_host("1", roles: [:b], steps: [1, 9]) expect(servers.count).to eq(1) expect(servers.roles_for([:b]).first.properties.steps).to eq([1, 3, 5, 1, 9]) end it "merges previously defined set properties" do servers.add_host("1", roles: [:b], endpoints: Set[123, 333]) servers.add_host("1", roles: [:b], endpoints: Set[222, 333]) expect(servers.count).to eq(1) expect(servers.roles_for([:b]).first.properties.endpoints).to eq(Set[123, 222, 333]) end it "adds array property value only ones for a new host" do servers.add_host("2", roles: [:array_test], array_property: [1, 2]) expect(servers.roles_for([:array_test]).first.properties.array_property).to eq [1, 2] end it "updates roles when custom user defined" do servers.add_host("1", roles: ["foo"], user: "custom") servers.add_host("1", roles: ["bar"], user: "custom") expect(servers.roles_for([:foo]).first.hostname).to eq "1" expect(servers.roles_for([:bar]).first.hostname).to eq "1" end it "updates roles when custom port defined" do servers.add_host("1", roles: ["foo"], port: 1234) servers.add_host("1", roles: ["bar"], port: 1234) expect(servers.roles_for([:foo]).first.hostname).to eq "1" expect(servers.roles_for([:bar]).first.hostname).to eq "1" end end describe "selecting roles" do before do servers.add_host("1", roles: :app, active: true) servers.add_host("2", roles: :app) end it "is empty if the filter would remove all matching hosts" do expect(servers.roles_for([:app, select: :inactive])).to be_empty end it "can filter hosts by properties on the host object using symbol as shorthand" do expect(servers.roles_for([:app, filter: :active]).length).to eq 1 end it "can select hosts by properties on the host object using symbol as shorthand" do expect(servers.roles_for([:app, select: :active]).length).to eq 1 end it "can filter hosts by properties on the host using a regular proc" do expect(servers.roles_for([:app, filter: ->(h) { h.properties.active }]).length).to eq 1 end it "can select hosts by properties on the host using a regular proc" do expect(servers.roles_for([:app, select: ->(h) { h.properties.active }]).length).to eq 1 end it "is empty if the regular proc filter would remove all matching hosts" do expect(servers.roles_for([:app, select: ->(h) { h.properties.inactive }])).to be_empty end end describe "excluding by property" do before do servers.add_host("1", roles: :app, active: true) servers.add_host("2", roles: :app, active: true, no_release: true) end it "is empty if the filter would remove all matching hosts" do hosts = servers.roles_for([:app, exclude: :active]) expect(hosts.map(&:hostname)).to be_empty end it "returns the servers without the attributes specified" do hosts = servers.roles_for([:app, exclude: :no_release]) expect(hosts.map(&:hostname)).to eq %w{1} end it "can exclude hosts by properties on the host using a regular proc" do hosts = servers.roles_for([:app, exclude: ->(h) { h.properties.no_release }]) expect(hosts.map(&:hostname)).to eq %w{1} end it "is empty if the regular proc filter would remove all matching hosts" do hosts = servers.roles_for([:app, exclude: ->(h) { h.properties.active }]) expect(hosts.map(&:hostname)).to be_empty end end describe "filtering roles internally" do before do servers.add_host("1", roles: :app, active: true) servers.add_host("2", roles: :app) servers.add_host("3", roles: :web) servers.add_host("4", roles: :web) servers.add_host("5", roles: :db) end subject { servers.roles_for(roles).map(&:hostname) } context "with the ROLES environment variable set" do before do ENV.stubs(:[]).with("ROLES").returns("web,db") ENV.stubs(:[]).with("HOSTS").returns(nil) end context "when selecting all roles" do let(:roles) { [:all] } it "ignores it" do expect(subject).to eq %w{1 2 3 4 5} end end context "when selecting specific roles" do let(:roles) { %i(app web) } it "ignores it" do expect(subject).to eq %w{1 2 3 4} end end context "when selecting roles not included in ROLE" do let(:roles) { [:app] } it "ignores it" do expect(subject).to eq %w{1 2} end end end context "with the HOSTS environment variable set" do before do ENV.stubs(:[]).with("ROLES").returns(nil) ENV.stubs(:[]).with("HOSTS").returns("3,5") end context "when selecting all roles" do let(:roles) { [:all] } it "ignores it" do expect(subject).to eq %w{1 2 3 4 5} end end context "when selecting specific roles" do let(:roles) { %i(app web) } it "ignores it" do expect(subject).to eq %w{1 2 3 4} end end context "when selecting no roles" do let(:roles) { [] } it "ignores it" do expect(subject).to be_empty end end end end end end end capistrano-3.10.0/spec/lib/capistrano/configuration/scm_resolver_spec.rb0000644000004100000410000000244213175754131026553 0ustar www-datawww-datarequire "spec_helper" module Capistrano class Configuration describe SCMResolver do include Capistrano::DSL let(:resolver) { SCMResolver.new } before do Rake::Task.define_task("deploy:check") Rake::Task.define_task("deploy:new_release_path") Rake::Task.define_task("deploy:set_current_revision") set :scm, SCMResolver::DEFAULT_GIT end after do Rake::Task.clear Capistrano::Configuration.reset! end context "default scm, no plugin installed" do it "emits a warning" do expect { resolver.resolve }.to output(/will not load the git scm/i).to_stderr end it "activates the git scm" do resolver.resolve expect(Rake::Task["git:wrapper"]).not_to be_nil end it "sets :scm to :git" do resolver.resolve expect(fetch(:scm)).to eq(:git) end end context "default scm, git plugin installed" do before do install_plugin Capistrano::SCM::Git end it "emits no warning" do expect { resolver.resolve }.not_to output.to_stderr end it "deletes :scm" do resolver.resolve expect(fetch(:scm)).to be_nil end end end end end capistrano-3.10.0/spec/lib/capistrano/configuration/filter_spec.rb0000644000004100000410000000666213175754131025345 0ustar www-datawww-datarequire "spec_helper" module Capistrano class Configuration describe Filter do let(:available) do [ Server.new("server1").add_roles(%i(web db)), Server.new("server2").add_role(:web), Server.new("server3").add_role(:redis), Server.new("server4").add_role(:db), Server.new("server5").add_role(:stageweb) ] end describe "#new" do it "won't create an invalid type of filter" do expect do Filter.new(:zarg) end.to raise_error RuntimeError end context "with type :host" do context "and no values" do it "creates an EmptyFilter strategy" do expect(Filter.new(:host).instance_variable_get(:@strategy)).to be_a(EmptyFilter) end end context "and :all" do it "creates an NullFilter strategy" do expect(Filter.new(:host, :all).instance_variable_get(:@strategy)).to be_a(NullFilter) end end context "and [:all]" do it "creates an NullFilter strategy" do expect(Filter.new(:host, [:all]).instance_variable_get(:@strategy)).to be_a(NullFilter) end end context "and [:all]" do it "creates an NullFilter strategy" do expect(Filter.new(:host, "all").instance_variable_get(:@strategy)).to be_a(NullFilter) end end end context "with type :role" do context "and no values" do it "creates an EmptyFilter strategy" do expect(Filter.new(:role).instance_variable_get(:@strategy)).to be_a(EmptyFilter) end end context "and :all" do it "creates an NullFilter strategy" do expect(Filter.new(:role, :all).instance_variable_get(:@strategy)).to be_a(NullFilter) end end context "and [:all]" do it "creates an NullFilter strategy" do expect(Filter.new(:role, [:all]).instance_variable_get(:@strategy)).to be_a(NullFilter) end end context "and [:all]" do it "creates an NullFilter strategy" do expect(Filter.new(:role, "all").instance_variable_get(:@strategy)).to be_a(NullFilter) end end end end describe "#filter" do let(:strategy) { filter.instance_variable_get(:@strategy) } let(:results) { mock("result") } shared_examples "it calls #filter on its strategy" do it "calls #filter on its strategy" do strategy.expects(:filter).with(available).returns(results) expect(filter.filter(available)).to eq(results) end end context "for an empty filter" do let(:filter) { Filter.new(:role) } it_behaves_like "it calls #filter on its strategy" end context "for a null filter" do let(:filter) { Filter.new(:role, :all) } it_behaves_like "it calls #filter on its strategy" end context "for a role filter" do let(:filter) { Filter.new(:role, "web") } it_behaves_like "it calls #filter on its strategy" end context "for a host filter" do let(:filter) { Filter.new(:host, "server1") } it_behaves_like "it calls #filter on its strategy" end end end end end capistrano-3.10.0/spec/lib/capistrano/configuration/role_filter_spec.rb0000644000004100000410000000500713175754131026356 0ustar www-datawww-datarequire "spec_helper" module Capistrano class Configuration describe RoleFilter do subject(:role_filter) { RoleFilter.new(values) } let(:available) do [ Server.new("server1").add_roles(%i(web db)), Server.new("server2").add_role(:web), Server.new("server3").add_role(:redis), Server.new("server4").add_role(:db), Server.new("server5").add_role(:stageweb), Server.new("server6").add_role(:"db.new") ] end shared_examples "it filters roles correctly" do |expected_size, expected| it "filters correctly" do set = role_filter.filter(available) expect(set.size).to eq(expected_size) expect(set.map(&:hostname)).to eq(expected) end end describe "#filter" do context "with a single role string" do let(:values) { "web" } it_behaves_like "it filters roles correctly", 2, %w{server1 server2} end context "with a single role" do let(:values) { [:web] } it_behaves_like "it filters roles correctly", 2, %w{server1 server2} end context "with multiple roles in a string" do let(:values) { "web,db" } it_behaves_like "it filters roles correctly", 3, %w{server1 server2 server4} end context "with multiple roles" do let(:values) { %i(web db) } it_behaves_like "it filters roles correctly", 3, %w{server1 server2 server4} end context "with a regex" do let(:values) { /red/ } it_behaves_like "it filters roles correctly", 1, %w{server3} end context "with a regex string" do let(:values) { "/red|web/" } it_behaves_like "it filters roles correctly", 4, %w{server1 server2 server3 server5} end context "with both a string and regex" do let(:values) { "db,/red/" } it_behaves_like "it filters roles correctly", 3, %w{server1 server3 server4} end context "with a dot wildcard" do let(:values) { "db.*" } it_behaves_like "it filters roles correctly", 0, %w{} end context "with a dot" do let(:values) { "db.new" } it_behaves_like "it filters roles correctly", 1, %w{server6} end context "with a dot wildcard regex" do let(:values) { "/db.*/" } it_behaves_like "it filters roles correctly", 3, %w{server1 server4 server6} end end end end end capistrano-3.10.0/spec/lib/capistrano/configuration/host_filter_spec.rb0000644000004100000410000000432713175754131026376 0ustar www-datawww-datarequire "spec_helper" module Capistrano class Configuration describe HostFilter do subject(:host_filter) { HostFilter.new(values) } let(:available) do [Server.new("server1"), Server.new("server2"), Server.new("server3"), Server.new("server4"), Server.new("server5")] end shared_examples "it filters hosts correctly" do |expected| it "filters correctly" do set = host_filter.filter(available) expect(set.map(&:hostname)).to eq(expected) end end describe "#filter" do context "with a string" do let(:values) { "server1" } it_behaves_like "it filters hosts correctly", %w{server1} context "and a single server" do let(:available) { Server.new("server1") } it_behaves_like "it filters hosts correctly", %w{server1} end end context "with a comma separated string" do let(:values) { "server1,server3" } it_behaves_like "it filters hosts correctly", %w{server1 server3} end context "with an array of strings" do let(:values) { %w{server1 server3} } it_behaves_like "it filters hosts correctly", %w{server1 server3} end context "with mixed splittable and unsplittable strings" do let(:values) { %w{server1 server2,server3} } it_behaves_like "it filters hosts correctly", %w{server1 server2 server3} end context "with a regexp" do let(:values) { "server[13]$" } it_behaves_like "it filters hosts correctly", %w{server1 server3} end context "with a regexp with line boundaries" do let(:values) { "^server" } it_behaves_like "it filters hosts correctly", %w{server1 server2 server3 server4 server5} end context "with a regexp with a comma" do let(:values) { 'server\d{1,3}$' } it_behaves_like "it filters hosts correctly", %w{server1 server2 server3 server4 server5} end context "without number" do let(:values) { "server" } it_behaves_like "it filters hosts correctly", %w{} end end end end end capistrano-3.10.0/spec/lib/capistrano/configuration/plugin_installer_spec.rb0000644000004100000410000000467213175754131027432 0ustar www-datawww-datarequire "spec_helper" require "capistrano/plugin" require "capistrano/scm/plugin" module Capistrano class Configuration class ExamplePlugin < Capistrano::Plugin def set_defaults set_if_empty :example_variable, "foo" end def define_tasks task :example task :example_prerequisite end def register_hooks before :example, :example_prerequisite end end class ExampleSCMPlugin < Capistrano::SCM::Plugin end describe PluginInstaller do include Capistrano::DSL let(:installer) { PluginInstaller.new } let(:options) { {} } let(:plugin) { ExamplePlugin.new } before do installer.install(plugin, **options) end after do Rake::Task.clear Capistrano::Configuration.reset! end context "installing plugin" do it "defines tasks" do expect(Rake::Task[:example]).to_not be_nil expect(Rake::Task[:example_prerequisite]).to_not be_nil end it "registers hooks" do task = Rake::Task[:example] expect(task.prerequisites).to eq([:example_prerequisite]) end it "sets defaults when load:defaults is invoked" do expect(fetch(:example_variable)).to be_nil invoke "load:defaults" expect(fetch(:example_variable)).to eq("foo") end it "doesn't say an SCM is installed" do expect(installer.scm_installed?).to be_falsey end end context "installing plugin class" do let(:plugin) { ExamplePlugin } it "defines tasks" do expect(Rake::Task[:example]).to_not be_nil expect(Rake::Task[:example_prerequisite]).to_not be_nil end end context "installing plugin without hooks" do let(:options) { { load_hooks: false } } it "doesn't register hooks" do task = Rake::Task[:example] expect(task.prerequisites).to be_empty end end context "installing plugin and loading immediately" do let(:options) { { load_immediately: true } } it "sets defaults immediately" do expect(fetch(:example_variable)).to eq("foo") end end context "installing an SCM plugin" do let(:plugin) { ExampleSCMPlugin } it "says an SCM is installed" do expect(installer.scm_installed?).to be_truthy end end end end end capistrano-3.10.0/spec/lib/capistrano/configuration/empty_filter_spec.rb0000644000004100000410000000053413175754131026553 0ustar www-datawww-datarequire "spec_helper" module Capistrano class Configuration describe EmptyFilter do subject(:empty_filter) { EmptyFilter.new } describe "#filter" do let(:servers) { mock("servers") } it "returns an empty array" do expect(empty_filter.filter(servers)).to eq([]) end end end end end capistrano-3.10.0/spec/lib/capistrano/configuration/question_spec.rb0000644000004100000410000000354413175754131025723 0ustar www-datawww-datarequire "spec_helper" module Capistrano class Configuration describe Question do let(:question) { Question.new(key, default, options) } let(:question_without_echo) { Question.new(key, default, echo: false) } let(:question_without_default) { Question.new(key, nil) } let(:default) { :default } let(:key) { :branch } let(:options) { nil } describe ".new" do it "takes a key, default, options" do question end end describe "#call" do context "value is entered" do let(:branch) { "branch" } it "returns the echoed value" do $stdout.expects(:print).with("Please enter branch (default): ") $stdin.expects(:gets).returns(branch) $stdin.expects(:noecho).never expect(question.call).to eq(branch) end it "returns the value but does not echo it" do $stdout.expects(:print).with("Please enter branch (default): ") $stdin.expects(:noecho).returns(branch) $stdout.expects(:print).with("\n") expect(question_without_echo.call).to eq(branch) end it "returns the value but has no default between parenthesis" do $stdout.expects(:print).with("Please enter branch: ") $stdin.expects(:gets).returns(branch) $stdin.expects(:noecho).never expect(question_without_default.call).to eq(branch) end end context "value is not entered" do let(:branch) { default } before do $stdout.expects(:print).with("Please enter branch (default): ") $stdin.expects(:gets).returns("") end it "returns the default as the value" do expect(question.call).to eq(branch) end end end end end end capistrano-3.10.0/spec/lib/capistrano/upload_task_spec.rb0000644000004100000410000000107113175754131023504 0ustar www-datawww-datarequire "spec_helper" describe Capistrano::UploadTask do let(:app) { Rake.application = Rake::Application.new } subject(:upload_task) { described_class.define_task("path/file.yml") } it { is_expected.to be_a(Rake::FileCreationTask) } it { is_expected.to be_needed } context "inside namespace" do let(:normal_task) { Rake::Task.define_task("path/other_file.yml") } around { |ex| app.in_namespace("namespace", &ex) } it { expect(upload_task.name).to eq("path/file.yml") } it { expect(upload_task.scope.path).to eq("namespace") } end end capistrano-3.10.0/spec/lib/capistrano/dsl_spec.rb0000644000004100000410000000537113175754131021767 0ustar www-datawww-datarequire "spec_helper" module Capistrano class DummyDSL include DSL end # see also - spec/integration/dsl_spec.rb describe DSL do let(:dsl) { DummyDSL.new } describe "#t" do before do I18n.expects(:t).with(:phrase, count: 2, scope: :capistrano) end it "delegates to I18n" do dsl.t(:phrase, count: 2) end end describe "#stage_set?" do subject { dsl.stage_set? } context "stage is set" do before do dsl.set(:stage, :sandbox) end it { expect(subject).to be_truthy } end context "stage is not set" do before do dsl.set(:stage, nil) end it { expect(subject).to be_falsey } end end describe "#sudo" do before do dsl.expects(:execute).with(:sudo, :my, :command) end it "prepends sudo, delegates to execute" do dsl.sudo(:my, :command) end end describe "#execute" do context "use outside of on scope" do after do task.clear Rake::Task.clear end let(:task) do Rake::Task.define_task("execute_outside_scope") do dsl.execute "whoami" end end it "prints helpful message to stderr" do expect do expect do task.invoke end.to output(/^.*Warning: `execute' should be wrapped in an `on' scope/).to_stderr end.to raise_error(NoMethodError) end end end describe "#invoke" do context "reinvoking" do it "will not reenable invoking task" do counter = 0 Rake::Task.define_task("A") do counter += 1 end expect do dsl.invoke("A") dsl.invoke("A") end.to change { counter }.by(1) end it "will print a message on stderr" do Rake::Task.define_task("B") expect do dsl.invoke("B") dsl.invoke("B") end.to output(/If you really meant to run this task again, use invoke!/).to_stderr end end end describe "#invoke!" do context "reinvoking" do it "will reenable invoking task" do counter = 0 Rake::Task.define_task("C") do counter += 1 end expect do dsl.invoke!("C") dsl.invoke!("C") end.to change { counter }.by(2) end it "will not print a message on stderr" do Rake::Task.define_task("D") expect do dsl.invoke!("D") dsl.invoke!("D") end.to_not output(/If you really meant to run this task again, use invoke!/).to_stderr end end end end end capistrano-3.10.0/spec/lib/capistrano/dsl/0000755000004100000410000000000013175754131020422 5ustar www-datawww-datacapistrano-3.10.0/spec/lib/capistrano/dsl/task_enhancements_spec.rb0000644000004100000410000000600313175754131025452 0ustar www-datawww-datarequire "spec_helper" module Capistrano class DummyTaskEnhancements include TaskEnhancements end describe TaskEnhancements do let(:task_enhancements) { DummyTaskEnhancements.new } describe "ordering" do after do task.clear before_task.clear after_task.clear Rake::Task.clear end let(:order) { [] } let!(:task) do Rake::Task.define_task("task", [:order]) do |_t, args| args["order"].push "task" end end let!(:before_task) do Rake::Task.define_task("before_task") do order.push "before_task" end end let!(:after_task) do Rake::Task.define_task("after_task") do order.push "after_task" end end it "invokes in proper order if define after than before" do task_enhancements.after("task", "after_task") task_enhancements.before("task", "before_task") Rake::Task["task"].invoke order expect(order).to eq(%w(before_task task after_task)) end it "invokes in proper order if define before than after" do task_enhancements.before("task", "before_task") task_enhancements.after("task", "after_task") Rake::Task["task"].invoke order expect(order).to eq(%w(before_task task after_task)) end it "invokes in proper order when referring to as-yet undefined tasks" do task_enhancements.after("task", "not_loaded_task") Rake::Task.define_task("not_loaded_task") do order.push "not_loaded_task" end Rake::Task["task"].invoke order expect(order).to eq(%w(task not_loaded_task)) end it "invokes in proper order and with arguments and block" do task_enhancements.after("task", "after_task_custom", :order) do |_t, _args| order.push "after_task" end task_enhancements.before("task", "before_task_custom", :order) do |_t, _args| order.push "before_task" end Rake::Task["task"].invoke(order) expect(order).to eq(%w(before_task task after_task)) end it "invokes using the correct namespace when defined within a namespace" do Rake.application.in_namespace("namespace") do Rake::Task.define_task("task") do |t| order.push(t.name) end task_enhancements.before("task", "before_task", :order) do |t| order.push(t.name) end task_enhancements.after("task", "after_task", :order) do |t| order.push(t.name) end end Rake::Task["namespace:task"].invoke expect(order).to eq( ["namespace:before_task", "namespace:task", "namespace:after_task"] ) end it "raises a sensible error if the task isn't found" do task_enhancements.after("task", "non_existent_task") expect { Rake::Task["task"].invoke order }.to raise_error(ArgumentError, 'Task "non_existent_task" not found') end end end end capistrano-3.10.0/spec/lib/capistrano/dsl/paths_spec.rb0000644000004100000410000001302013175754131023074 0ustar www-datawww-datarequire "spec_helper" describe Capistrano::DSL::Paths do let(:dsl) { Class.new.extend Capistrano::DSL } let(:parent) { Pathname.new("/var/shared") } let(:paths) { Class.new.extend Capistrano::DSL::Paths } let(:linked_dirs) { %w{log public/system} } let(:linked_files) { %w{config/database.yml log/my.log log/access.log} } before do dsl.set(:deploy_to, "/var/www") end describe "#linked_dirs" do subject { paths.linked_dirs(parent) } before do paths.expects(:fetch).with(:linked_dirs).returns(linked_dirs) end it "returns the full pathnames" do expect(subject).to eq [ Pathname.new("/var/shared/log"), Pathname.new("/var/shared/public/system") ] end end describe "#linked_files" do subject { paths.linked_files(parent) } before do paths.expects(:fetch).with(:linked_files).returns(linked_files) end it "returns the full pathnames" do expect(subject).to eq [ Pathname.new("/var/shared/config/database.yml"), Pathname.new("/var/shared/log/my.log"), Pathname.new("/var/shared/log/access.log") ] end end describe "#linked_file_dirs" do subject { paths.linked_file_dirs(parent) } before do paths.expects(:fetch).with(:linked_files).returns(linked_files) end it "returns the full paths names of the parent dirs" do expect(subject).to eq [ Pathname.new("/var/shared/config"), Pathname.new("/var/shared/log") ] end end describe "#linked_dir_parents" do subject { paths.linked_dir_parents(parent) } before do paths.expects(:fetch).with(:linked_dirs).returns(linked_dirs) end it "returns the full paths names of the parent dirs" do expect(subject).to eq [ Pathname.new("/var/shared"), Pathname.new("/var/shared/public") ] end end describe "#release path" do subject { dsl.release_path } context "where no release path has been set" do before do dsl.delete(:release_path) end it "returns the `current_path` value" do expect(subject.to_s).to eq "/var/www/current" end end context "where the release path has been set" do before do dsl.set(:release_path, "/var/www/release_path") end it "returns the set `release_path` value" do expect(subject.to_s).to eq "/var/www/release_path" end end end describe "#set_release_path" do let(:now) { Time.parse("Oct 21 16:29:00 2015") } subject { dsl.release_path } context "without a timestamp" do before do dsl.env.expects(:timestamp).returns(now) dsl.set_release_path end it "returns the release path with the current env timestamp" do expect(subject.to_s).to eq "/var/www/releases/20151021162900" end end context "with a timestamp" do before do dsl.set_release_path("timestamp") end it "returns the release path with the timestamp" do expect(subject.to_s).to eq "/var/www/releases/timestamp" end end end describe "#releases_path" do subject { paths.releases_path } context "with custom releases directory" do before do paths.expects(:fetch).with(:releases_directory, "releases").returns("test123") paths.expects(:fetch).with(:deploy_to).returns("/var/www") end it "returns the releases path with the custom directory" do expect(subject.to_s).to eq "/var/www/test123" end end end describe "#shared_path" do subject { paths.shared_path } context "with custom shared directory" do before do paths.expects(:fetch).with(:shared_directory, "shared").returns("test123") paths.expects(:fetch).with(:deploy_to).returns("/var/www") end it "returns the shared path with the custom directory" do expect(subject.to_s).to eq "/var/www/test123" end end end describe "#deploy_config_path" do subject { dsl.deploy_config_path.to_s } context "when not specified" do before do dsl.delete(:deploy_config_path) end it 'returns "config/deploy.rb"' do expect(subject).to eq "config/deploy.rb" end end context "when the variable :deploy_config_path is set" do before do dsl.set(:deploy_config_path, "my/custom/path.rb") end it "returns the custom path" do expect(subject).to eq "my/custom/path.rb" end end end describe "#stage_config_path" do subject { dsl.stage_config_path.to_s } context "when not specified" do before do dsl.delete(:stage_config_path) end it 'returns "config/deploy"' do expect(subject).to eq "config/deploy" end end context "when the variable :stage_config_path is set" do before do dsl.set(:stage_config_path, "my/custom/path") end it "returns the custom path" do expect(subject).to eq "my/custom/path" end end end describe "#repo_path" do subject { dsl.repo_path.to_s } context "when not specified" do before do dsl.delete(:repo_path) end it 'returns the default #{deploy_to}/repo' do dsl.set(:deploy_to, "/var/www") expect(subject).to eq "/var/www/repo" end end context "when the variable :repo_path is set" do before do dsl.set(:repo_path, "my/custom/path") end it "returns the custom path" do expect(subject).to eq "my/custom/path" end end end end capistrano-3.10.0/spec/lib/capistrano/application_spec.rb0000644000004100000410000000424313175754131023505 0ustar www-datawww-datarequire "spec_helper" describe Capistrano::Application do it "provides a --trace option which enables SSHKit/NetSSH trace output" it "provides a --format option which enables the choice of output formatting" let(:help_output) do out, _err = capture_io do flags "--help", "-h" end out end it "displays documentation URL as help banner" do expect(help_output.lines.first).to match(/capistranorb.com/) end %w(quiet silent verbose).each do |switch| it "doesn't include --#{switch} in help" do expect(help_output).not_to match(/--#{switch}/) end end it "overrides the rake method, but still prints the rake version" do out, _err = capture_io do flags "--version", "-V" end expect(out).to match(/\bCapistrano Version\b/) expect(out).to match(/\b#{Capistrano::VERSION}\b/) expect(out).to match(/\bRake Version\b/) expect(out).to match(/\b#{Rake::VERSION}\b/) end it "overrides the rake method, and sets the sshkit_backend to SSHKit::Backend::Printer" do capture_io do flags "--dry-run", "-n" end sshkit_backend = Capistrano::Configuration.fetch(:sshkit_backend) expect(sshkit_backend).to eq(SSHKit::Backend::Printer) end it "enables printing all config variables on command line parameter" do capture_io do flags "--print-config-variables", "-p" end expect(Capistrano::Configuration.fetch(:print_config_variables)).to be true end def flags(*sets) sets.each do |set| ARGV.clear @exit = catch(:system_exit) { command_line(*set) } end yield(subject.options) if block_given? end def command_line(*options) options.each { |opt| ARGV << opt } subject.define_singleton_method(:exit) do |*_args| throw(:system_exit, :exit) end subject.run subject.options end def capture_io require "stringio" orig_stdout = $stdout orig_stderr = $stderr captured_stdout = StringIO.new captured_stderr = StringIO.new $stdout = captured_stdout $stderr = captured_stderr yield return captured_stdout.string, captured_stderr.string ensure $stdout = orig_stdout $stderr = orig_stderr end end capistrano-3.10.0/spec/lib/capistrano/configuration_spec.rb0000644000004100000410000002361213175754131024052 0ustar www-datawww-datarequire "spec_helper" module Capistrano describe Configuration do let(:config) { Configuration.new } let(:servers) { stub } describe ".new" do it "accepts initial hash" do configuration = described_class.new(custom: "value") expect(configuration.fetch(:custom)).to eq("value") end end describe ".env" do it "is a global accessor to a single instance" do Configuration.env.set(:test, true) expect(Configuration.env.fetch(:test)).to be_truthy end end describe ".reset!" do it "blows away the existing `env` and creates a new one" do old_env = Configuration.env Configuration.reset! expect(Configuration.env).not_to be old_env end end describe "roles" do context "adding a role" do subject { config.role(:app, %w{server1 server2}) } before do Configuration::Servers.expects(:new).returns(servers) servers.expects(:add_role).with(:app, %w{server1 server2}, {}) end it "adds the role" do expect(subject) end end end describe "setting and fetching" do subject { config.fetch(:key, :default) } context "set" do it "sets by value" do config.set(:key, :value) expect(subject).to eq :value end it "sets by block" do config.set(:key) { :value } expect(subject).to eq :value end it "raises an exception when given both a value and block" do expect { config.set(:key, :value) { :value } }.to raise_error(Capistrano::ValidationError) end end context "set_if_empty" do it "sets by value when none is present" do config.set_if_empty(:key, :value) expect(subject).to eq :value end it "sets by block when none is present" do config.set_if_empty(:key) { :value } expect(subject).to eq :value end it "does not overwrite existing values" do config.set(:key, :value) config.set_if_empty(:key, :update) config.set_if_empty(:key) { :update } expect(subject).to eq :value end end context "value is not set" do it "returns the default value" do expect(subject).to eq :default end end context "value is a proc" do subject { config.fetch(:key, proc { :proc }) } it "calls the proc" do expect(subject).to eq :proc end end context "value is a lambda" do subject { config.fetch(:key, -> { :lambda }) } it "calls the lambda" do expect(subject).to eq :lambda end end context "value inside proc inside a proc" do subject { config.fetch(:key, proc { proc { "some value" } }) } it "calls all procs and lambdas" do expect(subject).to eq "some value" end end context "value inside lambda inside a lambda" do subject { config.fetch(:key, -> { -> { "some value" } }) } it "calls all procs and lambdas" do expect(subject).to eq "some value" end end context "value inside lambda inside a proc" do subject { config.fetch(:key, proc { -> { "some value" } }) } it "calls all procs and lambdas" do expect(subject).to eq "some value" end end context "value inside proc inside a lambda" do subject { config.fetch(:key, -> { proc { "some value" } }) } it "calls all procs and lambdas" do expect(subject).to eq "some value" end end context "lambda with parameters" do subject { config.fetch(:key, ->(c) { c }).call(42) } it "is returned as a lambda" do expect(subject).to eq 42 end end context "block is passed to fetch" do subject { config.fetch(:key, :default) { raise "we need this!" } } it "returns the block value" do expect { subject }.to raise_error(RuntimeError) end end context "validations" do before do config.validate :key do |_, value| raise Capistrano::ValidationError unless value.length > 3 end end it "validates string without error" do config.set(:key, "longer_value") end it "validates block without error" do config.set(:key) { "longer_value" } expect(config.fetch(:key)).to eq "longer_value" end it "validates lambda without error" do config.set :key, -> { "longer_value" } expect(config.fetch(:key)).to eq "longer_value" end it "raises an exception on invalid string" do expect { config.set(:key, "sho") }.to raise_error(Capistrano::ValidationError) end it "raises an exception on invalid string provided by block" do config.set(:key) { "sho" } expect { config.fetch(:key) }.to raise_error(Capistrano::ValidationError) end it "raises an exception on invalid string provided by lambda" do config.set :key, -> { "sho" } expect { config.fetch(:key) }.to raise_error(Capistrano::ValidationError) end end context "appending" do subject { config.append(:linked_dirs, "vendor/bundle", "tmp") } it "returns appended value" do expect(subject).to eq ["vendor/bundle", "tmp"] end context "on non-array variable" do before { config.set(:linked_dirs, "string") } subject { config.append(:linked_dirs, "vendor/bundle") } it "returns appended value" do expect(subject).to eq ["string", "vendor/bundle"] end end end context "removing" do before :each do config.set(:linked_dirs, ["vendor/bundle", "tmp"]) end subject { config.remove(:linked_dirs, "vendor/bundle") } it "returns without removed value" do expect(subject).to eq ["tmp"] end context "on non-array variable" do before { config.set(:linked_dirs, "string") } context "when removing same value" do subject { config.remove(:linked_dirs, "string") } it "returns without removed value" do expect(subject).to eq [] end end context "when removing different value" do subject { config.remove(:linked_dirs, "othervalue") } it "returns without removed value" do expect(subject).to eq ["string"] end end end end end describe "keys" do subject { config.keys } before do config.set(:key1, :value1) config.set(:key2, :value2) end it "returns all set keys" do expect(subject).to match_array %i(key1 key2) end end describe "deleting" do before do config.set(:key, :value) end it "deletes the value" do config.delete(:key) expect(config.fetch(:key)).to be_nil end end describe "asking" do let(:question) { stub } let(:options) { {} } before do Configuration::Question.expects(:new).with(:branch, :default, options) .returns(question) end it "prompts for the value when fetching" do config.ask(:branch, :default, options) expect(config.fetch(:branch)).to eq question end end describe "setting the backend" do it "by default, is SSHKit" do expect(config.backend).to eq SSHKit end it "can be set to another class" do config.backend = :test expect(config.backend).to eq :test end describe "ssh_options for Netssh" do it "merges them with the :ssh_options variable" do config.set :format, :pretty config.set :log_level, :debug config.set :ssh_options, user: "albert" SSHKit::Backend::Netssh.configure { |ssh| ssh.ssh_options = { password: "einstein" } } config.configure_backend expect( config.backend.config.backend.config.ssh_options ).to include(user: "albert", password: "einstein") end end end describe "dry_run?" do it "returns false when using default backend" do expect(config.dry_run?).to eq(false) end it "returns true when using printer backend" do config.set :sshkit_backend, SSHKit::Backend::Printer expect(config.dry_run?).to eq(true) end end describe "custom filtering" do it "accepts a custom filter object" do filter = Object.new def filter.filter(servers) servers end config.add_filter(filter) end it "accepts a custom filter as a block" do config.add_filter { |servers| servers } end it "raises an error if passed a block and an object" do filter = Object.new def filter.filter(servers) servers end expect { config.add_filter(filter) { |servers| servers } }.to raise_error(ArgumentError) end it "raises an error if the filter lacks a filter method" do filter = Object.new expect { config.add_filter(filter) }.to raise_error(TypeError) end it "calls the filter method of a custom filter" do ENV.delete "ROLES" ENV.delete "HOSTS" servers = Configuration::Servers.new servers.add_host("test1") servers.add_host("test2") servers.add_host("test3") filtered_servers = servers.take(2) filter = mock("custom filter") filter.expects(:filter) .with { |subset| subset.is_a? Configuration::Servers } .returns(filtered_servers) config.add_filter(filter) expect(config.filter(servers)).to eq(filtered_servers) end end end end capistrano-3.10.0/spec/lib/capistrano/doctor/0000755000004100000410000000000013175754131021132 5ustar www-datawww-datacapistrano-3.10.0/spec/lib/capistrano/doctor/gems_doctor_spec.rb0000644000004100000410000000362613175754131025005 0ustar www-datawww-datarequire "spec_helper" require "capistrano/doctor/gems_doctor" require "airbrussh/version" require "sshkit/version" require "net/ssh/version" module Capistrano module Doctor describe GemsDoctor do let(:doc) { GemsDoctor.new } it "prints using 4-space indentation" do expect { doc.call }.to output(/^ {4}/).to_stdout end it "prints the Capistrano version" do expect { doc.call }.to\ output(/capistrano\s+#{Regexp.quote(Capistrano::VERSION)}/).to_stdout end it "prints the Rake version" do expect { doc.call }.to\ output(/rake\s+#{Regexp.quote(Rake::VERSION)}/).to_stdout end it "prints the SSHKit version" do expect { doc.call }.to\ output(/sshkit\s+#{Regexp.quote(SSHKit::VERSION)}/).to_stdout end it "prints the Airbrussh version" do expect { doc.call }.to\ output(/airbrussh\s+#{Regexp.quote(Airbrussh::VERSION)}/).to_stdout end it "prints the net-ssh version" do expect { doc.call }.to\ output(/net-ssh\s+#{Regexp.quote(Net::SSH::Version::STRING)}/).to_stdout end it "warns that new version is available" do Gem.stubs(:latest_version_for).returns(Gem::Version.new("99.0.0")) expect { doc.call }.to output(/\(update available\)/).to_stdout end describe "Rake" do before do load File.expand_path("../../../../../lib/capistrano/doctor.rb", __FILE__) end after do Rake::Task.clear end it "has an doctor:gems task that calls GemsDoctor" do GemsDoctor.any_instance.expects(:call) Rake::Task["doctor:gems"].invoke end it "has a doctor task that depends on doctor:gems" do expect(Rake::Task["doctor"].prerequisites).to include("doctor:gems") end end end end end capistrano-3.10.0/spec/lib/capistrano/doctor/variables_doctor_spec.rb0000644000004100000410000000535713175754131026025 0ustar www-datawww-datarequire "spec_helper" require "capistrano/doctor/variables_doctor" module Capistrano module Doctor describe VariablesDoctor do include Capistrano::DSL let(:doc) { VariablesDoctor.new } before do set :branch, "master" set :pty, false env.variables.untrusted! do set :application, "my_app" set :repo_tree, "public" set :repo_url, ".git" set :copy_strategy, :scp set :custom_setting, "hello" set "string_setting", "hello" ask :secret end fetch :custom_setting end after { Capistrano::Configuration.reset! } it "prints using 4-space indentation" do expect { doc.call }.to output(/^ {4}/).to_stdout end it "prints variable names and values" do expect { doc.call }.to output(/:branch\s+"master"$/).to_stdout expect { doc.call }.to output(/:pty\s+false$/).to_stdout expect { doc.call }.to output(/:application\s+"my_app"$/).to_stdout expect { doc.call }.to output(/:repo_url\s+".git"$/).to_stdout expect { doc.call }.to output(/:repo_tree\s+"public"$/).to_stdout expect { doc.call }.to output(/:copy_strategy\s+:scp$/).to_stdout expect { doc.call }.to output(/:custom_setting\s+"hello"$/).to_stdout expect { doc.call }.to output(/"string_setting"\s+"hello"$/).to_stdout end it "prints unanswered question variable as " do expect { doc.call }.to output(/:secret\s+$/).to_stdout end it "prints warning for unrecognized variable" do expect { doc.call }.to \ output(/:copy_strategy is not a recognized Capistrano setting/)\ .to_stdout end it "does not print warning for unrecognized variable that is fetched" do expect { doc.call }.not_to \ output(/:custom_setting is not a recognized Capistrano setting/)\ .to_stdout end it "does not print warning for whitelisted variable" do expect { doc.call }.not_to \ output(/:repo_tree is not a recognized Capistrano setting/)\ .to_stdout end describe "Rake" do before do load File.expand_path("../../../../../lib/capistrano/doctor.rb", __FILE__) end after do Rake::Task.clear end it "has an doctor:variables task that calls VariablesDoctor" do VariablesDoctor.any_instance.expects(:call) Rake::Task["doctor:variables"].invoke end it "has a doctor task that depends on doctor:variables" do expect(Rake::Task["doctor"].prerequisites).to \ include("doctor:variables") end end end end end capistrano-3.10.0/spec/lib/capistrano/doctor/environment_doctor_spec.rb0000644000004100000410000000227113175754131026411 0ustar www-datawww-datarequire "spec_helper" require "capistrano/doctor/environment_doctor" module Capistrano module Doctor describe EnvironmentDoctor do let(:doc) { EnvironmentDoctor.new } it "prints using 4-space indentation" do expect { doc.call }.to output(/^ {4}/).to_stdout end it "prints the Ruby version" do expect { doc.call }.to\ output(/#{Regexp.quote(RUBY_DESCRIPTION)}/).to_stdout end it "prints the Rubygems version" do expect { doc.call }.to output(/#{Regexp.quote(Gem::VERSION)}/).to_stdout end describe "Rake" do before do load File.expand_path("../../../../../lib/capistrano/doctor.rb", __FILE__) end after do Rake::Task.clear end it "has an doctor:environment task that calls EnvironmentDoctor" do EnvironmentDoctor.any_instance.expects(:call) Rake::Task["doctor:environment"].invoke end it "has a doctor task that depends on doctor:environment" do expect(Rake::Task["doctor"].prerequisites).to \ include("doctor:environment") end end end end end capistrano-3.10.0/spec/lib/capistrano/doctor/output_helpers_spec.rb0000644000004100000410000000252613175754131025560 0ustar www-datawww-datarequire "spec_helper" require "capistrano/doctor/output_helpers" module Capistrano module Doctor describe OutputHelpers do include OutputHelpers # Force color for the purpose of these tests before { ENV.stubs(:[]).with("SSHKIT_COLOR").returns("1") } it "prints titles in blue with newlines and without indentation" do expect { title("Hello!") }.to\ output("\e[0;34;49m\nHello!\n\e[0m\n").to_stdout end it "prints warnings in yellow with 4-space indentation" do expect { warning("Yikes!") }.to\ output(" \e[0;33;49mYikes!\e[0m\n").to_stdout end it "overrides puts to indent 4 spaces per line" do expect { puts("one\ntwo") }.to output(" one\n two\n").to_stdout end it "formats tables with indent, aligned columns and per-row color" do data = [ ["one", ".", "1"], ["two", "..", "2"], ["three", "...", "3"] ] block = proc do |record, row| row.yellow if record.first == "two" row << record[0] row << record[1] row << record[2] end expected_output = <<-OUT one . 1 \e[0;33;49mtwo .. 2\e[0m three ... 3 OUT expect { table(data, &block) }.to output(expected_output).to_stdout end end end end capistrano-3.10.0/spec/lib/capistrano/doctor/servers_doctor_spec.rb0000644000004100000410000000506313175754131025540 0ustar www-datawww-datarequire "spec_helper" require "capistrano/doctor/servers_doctor" module Capistrano module Doctor describe ServersDoctor do include Capistrano::DSL let(:doc) { ServersDoctor.new } before { Capistrano::Configuration.reset! } after { Capistrano::Configuration.reset! } it "prints using 4-space indentation" do expect { doc.call }.to output(/^ {4}/).to_stdout end it "prints the number of defined servers" do role :app, %w(example.com) server "www@example.com:22" expect { doc.call }.to output(/Servers \(2\)/).to_stdout end describe "prints the server's details" do it "including username" do server "www@example.com" expect { doc.call }.to output(/www@example.com/).to_stdout end it "including port" do server "www@example.com:22" expect { doc.call }.to output(/www@example.com:22/).to_stdout end it "including roles" do role :app, %w(example.com) expect { doc.call }.to output(/example.com\s+\[:app\]/).to_stdout end it "including empty roles" do server "example.com" expect { doc.call }.to output(/example.com\s+\[\]/).to_stdout end it "including properties" do server "example.com", roles: %w(app db), primary: true expect { doc.call }.to \ output(/example.com\s+\[:app, :db\]\s+\{ :primary => true \}/).to_stdout end it "including misleading role name alert" do server "example.com", roles: ["web app db"] warning_msg = 'Whitespace detected in role(s) :"web app db". ' \ 'This might be a result of a mistyped "%w()" array literal' expect { doc.call }.to output(/#{Regexp.escape(warning_msg)}/).to_stdout end end it "doesn't fail for no servers" do expect { doc.call }.to output("\nServers (0)\n \n").to_stdout end describe "Rake" do before do load File.expand_path("../../../../../lib/capistrano/doctor.rb", __FILE__) end after do Rake::Task.clear end it "has an doctor:servers task that calls ServersDoctor" do ServersDoctor.any_instance.expects(:call) Rake::Task["doctor:servers"].invoke end it "has a doctor task that depends on doctor:servers" do expect(Rake::Task["doctor"].prerequisites).to \ include("doctor:servers") end end end end end capistrano-3.10.0/spec/lib/capistrano/plugin_spec.rb0000644000004100000410000000421513175754131022477 0ustar www-datawww-datarequire "spec_helper" require "capistrano/plugin" module Capistrano describe Plugin do include Rake::DSL include Capistrano::DSL class DummyPlugin < Capistrano::Plugin def define_tasks task :hello do end end def register_hooks before "deploy:published", "hello" end end class ExternalTasksPlugin < Capistrano::Plugin def define_tasks eval_rakefile( File.expand_path("../../../support/tasks/plugin.rake", __FILE__) ) end # Called from plugin.rake to demonstrate that helper methods work def hello set :plugin_result, "hello" end end before do # Define an example task to allow testing hooks task "deploy:published" end after do # Clean up any tasks or variables we created during the tests Rake::Task.clear Capistrano::Configuration.reset! end it "defines tasks when constructed" do install_plugin(DummyPlugin) expect(Rake::Task["hello"]).not_to be_nil end it "registers hooks when constructed" do install_plugin(DummyPlugin) expect(Rake::Task["deploy:published"].prerequisites).to include("hello") end it "skips registering hooks if load_hooks: false" do install_plugin(DummyPlugin, load_hooks: false) expect(Rake::Task["deploy:published"].prerequisites).to be_empty end it "doesn't call set_defaults immediately" do dummy = DummyPlugin.new install_plugin(dummy) dummy.expects(:set_defaults).never end it "calls set_defaults during load:defaults" do dummy = DummyPlugin.new dummy.expects(:set_defaults).once install_plugin(dummy) Rake::Task["load:defaults"].invoke end it "is able to load tasks from a .rake file" do install_plugin(ExternalTasksPlugin) Rake::Task["plugin_test"].invoke expect(fetch(:plugin_result)).to eq("hello") end it "exposes the SSHKit backend to subclasses" do SSHKit::Backend.expects(:current).returns(:backend) plugin = DummyPlugin.new expect(plugin.send(:backend)).to eq(:backend) end end end capistrano-3.10.0/spec/integration/0000755000004100000410000000000013175754131017252 5ustar www-datawww-datacapistrano-3.10.0/spec/integration/dsl_spec.rb0000644000004100000410000005053413175754131021402 0ustar www-datawww-datarequire "spec_helper" describe Capistrano::DSL do let(:dsl) { Class.new.extend Capistrano::DSL } before do Capistrano::Configuration.reset! end describe "setting and fetching hosts" do describe "when defining a host using the `server` syntax" do before do dsl.server "example1.com", roles: %w{web}, active: true dsl.server "example2.com", roles: %w{web} dsl.server "example3.com", roles: %w{app web}, active: true dsl.server "example4.com", roles: %w{app}, primary: true dsl.server "example5.com", roles: %w{db}, no_release: true, active: true end describe "fetching all servers" do subject { dsl.roles(:all) } it "returns all servers" do expect(subject.map(&:hostname)).to eq %w{example1.com example2.com example3.com example4.com example5.com} end end describe "fetching all release servers" do context "with no additional options" do subject { dsl.release_roles(:all) } it "returns all release servers" do expect(subject.map(&:hostname)).to eq %w{example1.com example2.com example3.com example4.com} end end context "with property filter options" do subject { dsl.release_roles(:all, filter: :active) } it "returns all release servers that match the property filter" do expect(subject.map(&:hostname)).to eq %w{example1.com example3.com} end end end describe "fetching servers by multiple roles" do it "does not confuse the last role with options" do expect(dsl.roles(:app, :web).count).to eq 4 expect(dsl.roles(:app, :web, filter: :active).count).to eq 2 end end describe "fetching servers by role" do subject { dsl.roles(:app) } it "returns the servers" do expect(subject.map(&:hostname)).to eq %w{example3.com example4.com} end end describe "fetching servers by an array of roles" do subject { dsl.roles([:app]) } it "returns the servers" do expect(subject.map(&:hostname)).to eq %w{example3.com example4.com} end end describe "fetching filtered servers by role" do subject { dsl.roles(:app, filter: :active) } it "returns the servers" do expect(subject.map(&:hostname)).to eq %w{example3.com} end end describe "fetching selected servers by role" do subject { dsl.roles(:app, select: :active) } it "returns the servers" do expect(subject.map(&:hostname)).to eq %w{example3.com} end end describe "fetching the primary server by role" do context "when inferring primary status based on order" do subject { dsl.primary(:web) } it "returns the servers" do expect(subject.hostname).to eq "example1.com" end end context "when the attribute `primary` is explicitly set" do subject { dsl.primary(:app) } it "returns the servers" do expect(subject.hostname).to eq "example4.com" end end end describe "setting an internal host filter" do subject { dsl.roles(:app) } it "is ignored" do dsl.set :filter, host: "example3.com" expect(subject.map(&:hostname)).to eq(["example3.com", "example4.com"]) end end describe "setting an internal role filter" do subject { dsl.roles(:app) } it "ignores it" do dsl.set :filter, role: :web expect(subject.map(&:hostname)).to eq(["example3.com", "example4.com"]) end end describe "setting an internal host and role filter" do subject { dsl.roles(:app) } it "ignores it" do dsl.set :filter, role: :web, host: "example1.com" expect(subject.map(&:hostname)).to eq(["example3.com", "example4.com"]) end end describe "setting an internal regexp host filter" do subject { dsl.roles(:all) } it "is ignored" do dsl.set :filter, host: /1/ expect(subject.map(&:hostname)).to eq(%w{example1.com example2.com example3.com example4.com example5.com}) end end describe "setting an internal hosts filter" do subject { dsl.roles(:app) } it "is ignored" do dsl.set :filter, hosts: "example3.com" expect(subject.map(&:hostname)).to eq(["example3.com", "example4.com"]) end end describe "setting an internal roles filter" do subject { dsl.roles(:app) } it "ignores it" do dsl.set :filter, roles: :web expect(subject.map(&:hostname)).to eq(["example3.com", "example4.com"]) end end describe "setting an internal hosts and roles filter" do subject { dsl.roles(:app) } it "ignores it" do dsl.set :filter, roles: :web, hosts: "example1.com" expect(subject.map(&:hostname)).to eq(["example3.com", "example4.com"]) end end describe "setting an internal regexp hosts filter" do subject { dsl.roles(:all) } it "is ignored" do dsl.set :filter, hosts: /1/ expect(subject.map(&:hostname)).to eq(%w{example1.com example2.com example3.com example4.com example5.com}) end end end describe "when defining role with reserved name" do it "fails with ArgumentError" do expect do dsl.role :all, %w{example1.com} end.to raise_error(ArgumentError, "all reserved name for role. Please choose another name") end end describe "when defining hosts using the `role` syntax" do before do dsl.role :web, %w{example1.com example2.com example3.com} dsl.role :web, %w{example1.com}, active: true dsl.role :app, %w{example3.com example4.com} dsl.role :app, %w{example3.com}, active: true dsl.role :app, %w{example4.com}, primary: true dsl.role :db, %w{example5.com}, no_release: true end describe "fetching all servers" do subject { dsl.roles(:all) } it "returns all servers" do expect(subject.map(&:hostname)).to eq %w{example1.com example2.com example3.com example4.com example5.com} end end describe "fetching all release servers" do context "with no additional options" do subject { dsl.release_roles(:all) } it "returns all release servers" do expect(subject.map(&:hostname)).to eq %w{example1.com example2.com example3.com example4.com} end end context "with filter options" do subject { dsl.release_roles(:all, filter: :active) } it "returns all release servers that match the filter" do expect(subject.map(&:hostname)).to eq %w{example1.com example3.com} end end end describe "fetching servers by role" do subject { dsl.roles(:app) } it "returns the servers" do expect(subject.map(&:hostname)).to eq %w{example3.com example4.com} end end describe "fetching servers by an array of roles" do subject { dsl.roles([:app]) } it "returns the servers" do expect(subject.map(&:hostname)).to eq %w{example3.com example4.com} end end describe "fetching filtered servers by role" do subject { dsl.roles(:app, filter: :active) } it "returns the servers" do expect(subject.map(&:hostname)).to eq %w{example3.com} end end describe "fetching selected servers by role" do subject { dsl.roles(:app, select: :active) } it "returns the servers" do expect(subject.map(&:hostname)).to eq %w{example3.com} end end describe "fetching the primary server by role" do context "when inferring primary status based on order" do subject { dsl.primary(:web) } it "returns the servers" do expect(subject.hostname).to eq "example1.com" end end context "when the attribute `primary` is explicity set" do subject { dsl.primary(:app) } it "returns the servers" do expect(subject.hostname).to eq "example4.com" end end end end describe "when defining a host using a combination of the `server` and `role` syntax" do before do dsl.server "db@example1.com:1234", roles: %w{db}, active: true dsl.server "root@example1.com:1234", roles: %w{web}, active: true dsl.server "example1.com:5678", roles: %w{web}, active: true dsl.role :app, %w{deployer@example1.com:1234} dsl.role :app, %w{example1.com:5678} end describe "fetching all servers" do it "creates one server per hostname, ignoring user combinations" do expect(dsl.roles(:all).size).to eq(2) end end describe "fetching servers for a role" do it "roles defined using the `server` syntax are included" do as = dsl.roles(:web).map { |server| "#{server.user}@#{server.hostname}:#{server.port}" } expect(as.size).to eq(2) expect(as[0]).to eq("deployer@example1.com:1234") expect(as[1]).to eq("@example1.com:5678") end it "roles defined using the `role` syntax are included" do as = dsl.roles(:app).map { |server| "#{server.user}@#{server.hostname}:#{server.port}" } expect(as.size).to eq(2) expect(as[0]).to eq("deployer@example1.com:1234") expect(as[1]).to eq("@example1.com:5678") end end end describe "when setting user and port" do subject { dsl.roles(:all).map { |server| "#{server.user}@#{server.hostname}:#{server.port}" }.first } describe "using the :user property" do it "takes precedence over in the host string" do dsl.server "db@example1.com:1234", roles: %w{db}, active: true, user: "brian" expect(subject).to eq("brian@example1.com:1234") end end describe "using the :port property" do it "takes precedence over in the host string" do dsl.server "db@example1.com:9090", roles: %w{db}, active: true, port: 1234 expect(subject).to eq("db@example1.com:1234") end end end end describe "setting and fetching variables" do before do dsl.set :scm, :git end context "without a default" do context "when the variables is defined" do it "returns the variable" do expect(dsl.fetch(:scm)).to eq :git end end context "when the variables is undefined" do it "returns nil" do expect(dsl.fetch(:source_control)).to be_nil end end end context "with a default" do context "when the variables is defined" do it "returns the variable" do expect(dsl.fetch(:scm, :svn)).to eq :git end end context "when the variables is undefined" do it "returns the default" do expect(dsl.fetch(:source_control, :svn)).to eq :svn end end end context "with a block" do context "when the variables is defined" do it "returns the variable" do expect(dsl.fetch(:scm) { :svn }).to eq :git end end context "when the variables is undefined" do it "calls the block" do expect(dsl.fetch(:source_control) { :svn }).to eq :svn end end end end describe "asking for a variable" do before do dsl.ask(:scm, :svn) $stdout.stubs(:print) end context "variable is provided" do before do $stdin.expects(:gets).returns("git") end it "sets the input as the variable" do expect(dsl.fetch(:scm)).to eq "git" end end context "variable is not provided" do before do $stdin.expects(:gets).returns("") end it "sets the variable as the default" do expect(dsl.fetch(:scm)).to eq :svn end end end describe "checking for presence" do subject { dsl.any? :linked_files } before do dsl.set(:linked_files, linked_files) end context "variable is an non-empty array" do let(:linked_files) { %w{1} } it { expect(subject).to be_truthy } end context "variable is an empty array" do let(:linked_files) { [] } it { expect(subject).to be_falsey } end context "variable exists, is not an array" do let(:linked_files) { stub } it { expect(subject).to be_truthy } end context "variable is nil" do let(:linked_files) { nil } it { expect(subject).to be_falsey } end end describe "configuration SSHKit" do let(:config) { SSHKit.config } let(:backend) { SSHKit.config.backend.config } let(:default_env) { { rails_env: :production } } before do dsl.set(:format, :dot) dsl.set(:log_level, :debug) dsl.set(:default_env, default_env) dsl.set(:pty, true) dsl.set(:connection_timeout, 10) dsl.set(:ssh_options, keys: %w(/home/user/.ssh/id_rsa), forward_agent: false, auth_methods: %w(publickey password)) dsl.configure_backend end it "sets the output" do expect(config.output).to be_a SSHKit::Formatter::Dot end it "sets the output verbosity" do expect(config.output_verbosity).to eq 0 end it "sets the default env" do expect(config.default_env).to eq default_env end it "sets the backend pty" do expect(backend.pty).to be_truthy end it "sets the backend connection timeout" do expect(backend.connection_timeout).to eq 10 end it "sets the backend ssh_options" do expect(backend.ssh_options[:keys]).to eq %w(/home/user/.ssh/id_rsa) expect(backend.ssh_options[:forward_agent]).to eq false expect(backend.ssh_options[:auth_methods]).to eq %w(publickey password) end end describe "on()" do describe "when passed server objects" do before do dsl.server "example1.com", roles: %w{web}, active: true dsl.server "example2.com", roles: %w{web} dsl.server "example3.com", roles: %w{app web}, active: true dsl.server "example4.com", roles: %w{app}, primary: true dsl.server "example5.com", roles: %w{db}, no_release: true @coordinator = mock("coordinator") @coordinator.expects(:each).returns(nil) ENV.delete "ROLES" ENV.delete "HOSTS" end it "filters by role from the :filter variable" do hosts = dsl.roles(:web) all = dsl.roles(:all) SSHKit::Coordinator.expects(:new).with(hosts).returns(@coordinator) dsl.set :filter, role: "web" dsl.on(all) end it "filters by host and role from the :filter variable" do all = dsl.roles(:all) SSHKit::Coordinator.expects(:new).with([]).returns(@coordinator) dsl.set :filter, role: "db", host: "example3.com" dsl.on(all) end it "filters by roles from the :filter variable" do hosts = dsl.roles(:web) all = dsl.roles(:all) SSHKit::Coordinator.expects(:new).with(hosts).returns(@coordinator) dsl.set :filter, roles: "web" dsl.on(all) end it "filters by hosts and roles from the :filter variable" do all = dsl.roles(:all) SSHKit::Coordinator.expects(:new).with([]).returns(@coordinator) dsl.set :filter, roles: "db", hosts: "example3.com" dsl.on(all) end it "filters from ENV[ROLES]" do hosts = dsl.roles(:db) all = dsl.roles(:all) SSHKit::Coordinator.expects(:new).with(hosts).returns(@coordinator) ENV["ROLES"] = "db" dsl.on(all) end it "filters from ENV[HOSTS]" do hosts = dsl.roles(:db) all = dsl.roles(:all) SSHKit::Coordinator.expects(:new).with(hosts).returns(@coordinator) ENV["HOSTS"] = "example5.com" dsl.on(all) end it "filters by ENV[HOSTS] && ENV[ROLES]" do all = dsl.roles(:all) SSHKit::Coordinator.expects(:new).with([]).returns(@coordinator) ENV["HOSTS"] = "example5.com" ENV["ROLES"] = "web" dsl.on(all) end end describe "when passed server literal names" do before do ENV.delete "ROLES" ENV.delete "HOSTS" @coordinator = mock("coordinator") @coordinator.expects(:each).returns(nil) end it "selects nothing when a role filter is present" do dsl.set :filter, role: "web" SSHKit::Coordinator.expects(:new).with([]).returns(@coordinator) dsl.on("my.server") end it "selects using the string when a host filter is present" do dsl.set :filter, host: "server.local" SSHKit::Coordinator.expects(:new).with(["server.local"]).returns(@coordinator) dsl.on("server.local") end it "doesn't select when a host filter is present that doesn't match" do dsl.set :filter, host: "ruby.local" SSHKit::Coordinator.expects(:new).with([]).returns(@coordinator) dsl.on("server.local") end it "selects nothing when a roles filter is present" do dsl.set :filter, roles: "web" SSHKit::Coordinator.expects(:new).with([]).returns(@coordinator) dsl.on("my.server") end it "selects using the string when a hosts filter is present" do dsl.set :filter, hosts: "server.local" SSHKit::Coordinator.expects(:new).with(["server.local"]).returns(@coordinator) dsl.on("server.local") end it "doesn't select when a hosts filter is present that doesn't match" do dsl.set :filter, hosts: "ruby.local" SSHKit::Coordinator.expects(:new).with([]).returns(@coordinator) dsl.on("server.local") end end end describe "role_properties()" do before do dsl.role :redis, %w[example1.com example2.com], redis: { port: 6379, type: :slave } dsl.server "example1.com", roles: %w{web}, active: true, web: { port: 80 } dsl.server "example2.com", roles: %w{web redis}, web: { port: 81 }, redis: { type: :master } dsl.server "example3.com", roles: %w{app}, primary: true end it "retrieves properties for a single role as a set" do rps = dsl.role_properties(:app) expect(rps).to eq(Set[{ hostname: "example3.com", role: :app }]) end it "retrieves properties for multiple roles as a set" do rps = dsl.role_properties(:app, :web) expect(rps).to eq(Set[{ hostname: "example3.com", role: :app }, { hostname: "example1.com", role: :web, port: 80 }, { hostname: "example2.com", role: :web, port: 81 }]) end it "yields the properties for a single role" do recipient = mock("recipient") recipient.expects(:doit).with("example1.com", :redis, port: 6379, type: :slave) recipient.expects(:doit).with("example2.com", :redis, port: 6379, type: :master) dsl.role_properties(:redis) do |host, role, props| recipient.doit(host, role, props) end end it "yields the properties for multiple roles" do recipient = mock("recipient") recipient.expects(:doit).with("example1.com", :redis, port: 6379, type: :slave) recipient.expects(:doit).with("example2.com", :redis, port: 6379, type: :master) recipient.expects(:doit).with("example3.com", :app, nil) dsl.role_properties(:redis, :app) do |host, role, props| recipient.doit(host, role, props) end end it "yields the merged properties for multiple roles" do recipient = mock("recipient") recipient.expects(:doit).with("example1.com", :redis, port: 6379, type: :slave) recipient.expects(:doit).with("example2.com", :redis, port: 6379, type: :master) recipient.expects(:doit).with("example1.com", :web, port: 80) recipient.expects(:doit).with("example2.com", :web, port: 81) dsl.role_properties(:redis, :web) do |host, role, props| recipient.doit(host, role, props) end end it "honours a property filter before yielding" do recipient = mock("recipient") recipient.expects(:doit).with("example1.com", :redis, port: 6379, type: :slave) recipient.expects(:doit).with("example1.com", :web, port: 80) dsl.role_properties(:redis, :web, select: :active) do |host, role, props| recipient.doit(host, role, props) end end end end capistrano-3.10.0/spec/integration_spec_helper.rb0000644000004100000410000000013513175754131022147 0ustar www-datawww-datarequire "spec_helper" require "support/test_app" require "support/matchers" include TestApp capistrano-3.10.0/spec/support/0000755000004100000410000000000013175754131016443 5ustar www-datawww-datacapistrano-3.10.0/spec/support/matchers.rb0000644000004100000410000000017213175754131020576 0ustar www-datawww-dataRSpec::Matchers.define :be_a_symlink_to do |expected| match do |actual| File.identical?(expected, actual) end end capistrano-3.10.0/spec/support/test_app.rb0000644000004100000410000000777513175754131020627 0ustar www-datawww-datarequire "English" require "fileutils" require "pathname" module TestApp extend self def install install_test_app_with(default_config) end def default_config <<-CONFIG set :deploy_to, '#{deploy_to}' set :repo_url, 'git://github.com/capistrano/capistrano.git' set :branch, 'master' set :ssh_options, { keys: "\#{ENV['HOME']}/.vagrant.d/insecure_private_key", auth_methods: ['publickey'] } server 'vagrant@localhost:2220', roles: %w{web app} set :linked_files, #{linked_files} set :linked_dirs, #{linked_dirs} set :format_options, log_file: nil set :local_user, #{current_user.inspect} CONFIG end def linked_files %w{config/database.yml} end def linked_file shared_path.join(linked_files.first) end def linked_dirs %w{bin log public/system} end def create_test_app FileUtils.rm_rf(test_app_path) FileUtils.mkdir(test_app_path) File.open(gemfile, "w+") do |file| file.write "gem 'capistrano', path: '#{path_to_cap}'" end Dir.chdir(test_app_path) do run "bundle" end end def install_test_app_with(config) create_test_app Dir.chdir(test_app_path) do run "cap install STAGES=#{stage}" end write_local_deploy_file(config) end def write_local_deploy_file(config) File.open(test_stage_path, "w") do |file| file.write config end end def write_local_stage_file(filename, config=nil) File.open(test_app_path.join("config/deploy/#{filename}"), "w") do |file| file.write(config) if config end end def append_to_deploy_file(config) File.open(test_stage_path, "a") do |file| file.write config + "\n" end end def prepend_to_capfile(config) current_capfile = File.read(capfile) File.open(capfile, "w") do |file| file.write config file.write current_capfile end end def create_shared_directory(path) FileUtils.mkdir_p(shared_path.join(path)) end def create_shared_file(path) File.open(shared_path.join(path), "w") end def cap(task, subdirectory=nil) run "cap #{stage} #{task} --trace", subdirectory end def run(command, subdirectory=nil) output = nil command = "bundle exec #{command}" unless command =~ /^bundle\b/ dir = subdirectory ? test_app_path.join(subdirectory) : test_app_path Dir.chdir(dir) do output = with_clean_bundler_env { `#{command}` } end [$CHILD_STATUS.success?, output] end def stage "test" end def test_stage_path test_app_path.join("config/deploy/test.rb") end def test_app_path Pathname.new("/tmp/test_app") end def deploy_to Pathname.new("/home/vagrant/var/www/deploy") end def shared_path deploy_to.join("shared") end def current_path deploy_to.join("current") end def releases_path deploy_to.join("releases") end def release_path(t=timestamp) releases_path.join(t) end def timestamp(offset=0) (Time.now.utc + offset).strftime("%Y%m%d%H%M%S") end def repo_path deploy_to.join("repo") end def path_to_cap File.expand_path(".") end def gemfile test_app_path.join("Gemfile") end def capfile test_app_path.join("Capfile") end def current_user "(GitHub Web Flow) via ShipIt" end def task_dir test_app_path.join("lib/capistrano/tasks") end def copy_task_to_test_app(source) FileUtils.cp(source, task_dir) end def config_path test_app_path.join("config") end def move_configuration_to_custom_location(location) prepend_to_capfile( <<-CONFIG set :stage_config_path, "app/config/deploy" set :deploy_config_path, "app/config/deploy.rb" CONFIG ) location = test_app_path.join(location) FileUtils.mkdir_p(location) FileUtils.mv(config_path, location) end def git_wrapper_path "/tmp/git-ssh-my_app_name-#{stage}-#{current_user}.sh" end def with_clean_bundler_env(&block) return yield unless defined?(Bundler) Bundler.with_clean_env(&block) end end capistrano-3.10.0/spec/support/.gitignore0000644000004100000410000000001113175754131020423 0ustar www-datawww-data.vagrant capistrano-3.10.0/spec/support/Vagrantfile0000644000004100000410000000142313175754131020630 0ustar www-datawww-datarequire "open-uri" Vagrant.configure("2") do |config| config.ssh.insert_key = false [:app].each_with_index do |role, i| config.vm.define(role, primary: true) do |primary| primary.vm.define role primary.vm.box = "hashicorp/precise64" primary.vm.network "forwarded_port", guest: 22, host: "222#{i}".to_i primary.vm.provision :shell, inline: "sudo apt-get -y install git-core" vagrantkey = open("https://raw.githubusercontent.com/mitchellh/vagrant/master/keys/vagrant.pub", "r", &:read) primary.vm.provision :shell, inline: <<-INLINE install -d -m 700 /root/.ssh echo -e "#{vagrantkey}" > /root/.ssh/authorized_keys chmod 0600 /root/.ssh/authorized_keys INLINE end end end capistrano-3.10.0/spec/support/tasks/0000755000004100000410000000000013175754131017570 5ustar www-datawww-datacapistrano-3.10.0/spec/support/tasks/fail.rake0000644000004100000410000000027413175754131021352 0ustar www-datawww-dataset :fail, proc { raise } before "deploy:starting", :fail do on roles :all do execute :mkdir, "-p", shared_path execute :touch, shared_path.join("fail") end fetch(:fail) end capistrano-3.10.0/spec/support/tasks/plugin.rake0000644000004100000410000000021513175754131021730 0ustar www-datawww-data# This rake file is used by plugin_spec.rb. task :plugin_test do # Example of invoking a helper method provided by the plugin hello end capistrano-3.10.0/spec/support/tasks/database.rake0000644000004100000410000000035013175754131022176 0ustar www-datawww-datanamespace :deploy do namespace :check do task linked_files: "config/database.yml" end end remote_file "config/database.yml" => "/tmp/database.yml", :roles => :all file "/tmp/database.yml" do |t| sh "touch #{t.name}" end capistrano-3.10.0/spec/support/tasks/root.rake0000644000004100000410000000033713175754131021422 0ustar www-datawww-datatask :am_i_root do on roles(:all) do |host| host.user = "root" ident = capture :id, "-a" info "I am #{ident}" end on roles(:all) do |_host| ident = capture :id, "-a" info "I am #{ident}" end end capistrano-3.10.0/spec/support/tasks/failed.rake0000644000004100000410000000016513175754131021662 0ustar www-datawww-dataafter "deploy:failed", :custom_failed do on roles :all do execute :touch, shared_path.join("failed") end end capistrano-3.10.0/.travis.yml0000644000004100000410000000113213175754131016103 0ustar www-datawww-datalanguage: ruby rvm: - 2.4.1 - 2.3.1 - 2.2 - 2.1 - 2.0 matrix: # Rubinius periodically fails in general, and it always segfaults for RuboCop # in particular. Until we figure out how to make it work consistently, allow # it to fail without breaking the build. allow_failures: - rvm: rbx-2 include: - rvm: rbx-2 script: bundle exec rake spec # Run Danger only once, on 2.3.1 - rvm: 2.3.1 before_script: bundle exec danger script: bundle exec rake spec rubocop install: bundle install --jobs=1 cache: bundler branches: except: - legacy-v2 sudo: false capistrano-3.10.0/lib/0000755000004100000410000000000013175754131014543 5ustar www-datawww-datacapistrano-3.10.0/lib/Capfile0000644000004100000410000000011013175754131016021 0ustar www-datawww-data#!/usr/bin/env cap include Capistrano::DSL require "capistrano/install" capistrano-3.10.0/lib/capistrano/0000755000004100000410000000000013175754131016706 5ustar www-datawww-datacapistrano-3.10.0/lib/capistrano/version_validator.rb0000644000004100000410000000102013175754131022756 0ustar www-datawww-datamodule Capistrano class VersionValidator def initialize(version) @version = version end def verify return self if match? raise "Capfile locked at #{version}, but #{current_version} is loaded" end private attr_reader :version def match? available =~ requested end def current_version VERSION end def available Gem::Dependency.new("cap", version) end def requested Gem::Dependency.new("cap", current_version) end end end capistrano-3.10.0/lib/capistrano/scm/0000755000004100000410000000000013175754131017470 5ustar www-datawww-datacapistrano-3.10.0/lib/capistrano/scm/git.rb0000644000004100000410000000527713175754131020613 0ustar www-datawww-datarequire "capistrano/scm/plugin" require "cgi" require "shellwords" require "uri" class Capistrano::SCM::Git < Capistrano::SCM::Plugin def set_defaults set_if_empty :git_shallow_clone, false set_if_empty :git_wrapper_path, lambda { # Try to avoid permissions issues when multiple users deploy the same app # by using different file names in the same dir for each deployer and stage. suffix = %i(application stage local_user).map { |key| fetch(key).to_s }.join("-") "#{fetch(:tmp_dir)}/git-ssh-#{suffix}.sh" } set_if_empty :git_environmental_variables, lambda { { git_askpass: "/bin/echo", git_ssh: fetch(:git_wrapper_path) } } end def register_hooks after "deploy:new_release_path", "git:create_release" before "deploy:check", "git:check" before "deploy:set_current_revision", "git:set_current_revision" end def define_tasks eval_rakefile File.expand_path("../tasks/git.rake", __FILE__) end def repo_mirror_exists? backend.test " [ -f #{repo_path}/HEAD ] " end def check_repo_is_reachable git :'ls-remote', git_repo_url, "HEAD" end def clone_repo if (depth = fetch(:git_shallow_clone)) git :clone, "--mirror", "--depth", depth, "--no-single-branch", git_repo_url, repo_path.to_s else git :clone, "--mirror", git_repo_url, repo_path.to_s end end def update_mirror # Update the origin URL if necessary. git :remote, "set-url", "origin", git_repo_url # Note: Requires git version 1.9 or greater if (depth = fetch(:git_shallow_clone)) git :fetch, "--depth", depth, "origin", fetch(:branch) else git :remote, :update, "--prune" end end def archive_to_release_path if (tree = fetch(:repo_tree)) tree = tree.slice %r#^/?(.*?)/?$#, 1 components = tree.split("/").size git :archive, fetch(:branch), tree, "| #{SSHKit.config.command_map[:tar]} -x --strip-components #{components} -f - -C", release_path else git :archive, fetch(:branch), "| #{SSHKit.config.command_map[:tar]} -x -f - -C", release_path end end def fetch_revision backend.capture(:git, "rev-list --max-count=1 #{fetch(:branch)}") end def git(*args) args.unshift :git backend.execute(*args) end def git_repo_url if fetch(:git_http_username) && fetch(:git_http_password) URI.parse(repo_url).tap do |repo_uri| repo_uri.user = fetch(:git_http_username) repo_uri.password = CGI.escape(fetch(:git_http_password)) end.to_s elsif fetch(:git_http_username) URI.parse(repo_url).tap do |repo_uri| repo_uri.user = fetch(:git_http_username) end.to_s else repo_url end end end capistrano-3.10.0/lib/capistrano/scm/svn.rb0000644000004100000410000000310413175754131020621 0ustar www-datawww-datarequire "capistrano/scm/plugin" class Capistrano::SCM::Svn < Capistrano::SCM::Plugin def register_hooks after "deploy:new_release_path", "svn:create_release" before "deploy:check", "svn:check" before "deploy:set_current_revision", "svn:set_current_revision" end def define_tasks eval_rakefile File.expand_path("../tasks/svn.rake", __FILE__) end def svn(*args) args.unshift(:svn) args.push "--username #{fetch(:svn_username)}" if fetch(:svn_username) args.push "--password #{fetch(:svn_password)}" if fetch(:svn_password) args.push "--revision #{fetch(:svn_revision)}" if fetch(:svn_revision) backend.execute(*args) end def repo_mirror_exists? backend.test " [ -d #{repo_path}/.svn ] " end def check_repo_is_reachable svn_username = fetch(:svn_username) ? "--username #{fetch(:svn_username)}" : "" svn_password = fetch(:svn_password) ? "--password #{fetch(:svn_password)}" : "" backend.test :svn, :info, repo_url, svn_username, svn_password end def clone_repo svn :checkout, repo_url, repo_path.to_s end def update_mirror # Switch the repository URL if necessary. repo_mirror_url = fetch_repo_mirror_url svn :switch, repo_url unless repo_mirror_url == repo_url svn :update end def archive_to_release_path svn :export, "--force", ".", release_path end def fetch_revision backend.capture(:svnversion, repo_path.to_s) end def fetch_repo_mirror_url backend.capture(:svn, :info, repo_path.to_s).each_line do |line| return $1 if /\AURL: (.*)\n\z/ =~ line end end end capistrano-3.10.0/lib/capistrano/scm/hg.rb0000644000004100000410000000254313175754131020417 0ustar www-datawww-datarequire "capistrano/scm/plugin" require "securerandom" class Capistrano::SCM::Hg < Capistrano::SCM::Plugin def register_hooks after "deploy:new_release_path", "hg:create_release" before "deploy:check", "hg:check" before "deploy:set_current_revision", "hg:set_current_revision" end def define_tasks eval_rakefile File.expand_path("../tasks/hg.rake", __FILE__) end def hg(*args) args.unshift(:hg) backend.execute(*args) end def repo_mirror_exists? backend.test " [ -d #{repo_path}/.hg ] " end def check_repo_is_reachable hg "id", repo_url end def clone_repo hg "clone", "--noupdate", repo_url, repo_path.to_s end def update_mirror hg "pull" end def archive_to_release_path if (tree = fetch(:repo_tree)) tree = tree.slice %r#^/?(.*?)/?$#, 1 components = tree.split("/").size temp_tar = "#{fetch(:tmp_dir)}/#{SecureRandom.hex(10)}.tar" hg "archive -p . -I", tree, "--rev", fetch(:branch), temp_tar backend.execute :mkdir, "-p", release_path backend.execute :tar, "-x --strip-components #{components} -f", temp_tar, "-C", release_path backend.execute :rm, temp_tar else hg "archive", release_path, "--rev", fetch(:branch) end end def fetch_revision backend.capture(:hg, "log --rev #{fetch(:branch)} --template \"{node}\n\"") end end capistrano-3.10.0/lib/capistrano/scm/plugin.rb0000644000004100000410000000064713175754131021322 0ustar www-datawww-datarequire "capistrano/plugin" require "capistrano/scm" # Base class for all built-in and third-party SCM plugins. Notice that this # class doesn't really do anything other than provide an `scm?` predicate. This # tells Capistrano that the plugin provides SCM functionality. All other plugin # features are inherited from Capistrano::Plugin. # class Capistrano::SCM::Plugin < Capistrano::Plugin def scm? true end end capistrano-3.10.0/lib/capistrano/scm/tasks/0000755000004100000410000000000013175754131020615 5ustar www-datawww-datacapistrano-3.10.0/lib/capistrano/scm/tasks/hg.rake0000644000004100000410000000222613175754131022061 0ustar www-datawww-data# TODO: this is nearly identical to git.rake. DRY up? # This trick lets us access the Hg plugin within `on` blocks. hg_plugin = self namespace :hg do desc "Check that the repo is reachable" task :check do on release_roles :all do hg_plugin.check_repo_is_reachable end end desc "Clone the repo to the cache" task :clone do on release_roles :all do if hg_plugin.repo_mirror_exists? info t(:mirror_exists, at: repo_path) else within deploy_path do hg_plugin.clone_repo end end end end desc "Pull changes from the remote repo" task update: :'hg:clone' do on release_roles :all do within repo_path do hg_plugin.update_mirror end end end desc "Copy repo to releases" task create_release: :'hg:update' do on release_roles :all do within repo_path do hg_plugin.archive_to_release_path end end end desc "Determine the revision that will be deployed" task :set_current_revision do on release_roles :all do within repo_path do set :current_revision, hg_plugin.fetch_revision end end end end capistrano-3.10.0/lib/capistrano/scm/tasks/svn.rake0000644000004100000410000000224113175754131022266 0ustar www-datawww-data# TODO: this is nearly identical to git.rake. DRY up? # This trick lets us access the Svn plugin within `on` blocks. svn_plugin = self namespace :svn do desc "Check that the repo is reachable" task :check do on release_roles :all do svn_plugin.check_repo_is_reachable end end desc "Clone the repo to the cache" task :clone do on release_roles :all do if svn_plugin.repo_mirror_exists? info t(:mirror_exists, at: repo_path) else within deploy_path do svn_plugin.clone_repo end end end end desc "Pull changes from the remote repo" task update: :'svn:clone' do on release_roles :all do within repo_path do svn_plugin.update_mirror end end end desc "Copy repo to releases" task create_release: :'svn:update' do on release_roles :all do within repo_path do svn_plugin.archive_to_release_path end end end desc "Determine the revision that will be deployed" task :set_current_revision do on release_roles :all do within repo_path do set :current_revision, svn_plugin.fetch_revision end end end end capistrano-3.10.0/lib/capistrano/scm/tasks/git.rake0000644000004100000410000000401713175754131022246 0ustar www-datawww-data# This trick lets us access the Git plugin within `on` blocks. git_plugin = self namespace :git do desc "Upload the git wrapper script, this script guarantees that we can script git without getting an interactive prompt" task :wrapper do on release_roles :all do execute :mkdir, "-p", File.dirname(fetch(:git_wrapper_path)).shellescape upload! StringIO.new("#!/bin/sh -e\nexec /usr/bin/ssh -o PasswordAuthentication=no -o StrictHostKeyChecking=no \"$@\"\n"), fetch(:git_wrapper_path) execute :chmod, "700", fetch(:git_wrapper_path).shellescape end end desc "Check that the repository is reachable" task check: :'git:wrapper' do fetch(:branch) on release_roles :all do with fetch(:git_environmental_variables) do git_plugin.check_repo_is_reachable end end end desc "Clone the repo to the cache" task clone: :'git:wrapper' do on release_roles :all do if git_plugin.repo_mirror_exists? info t(:mirror_exists, at: repo_path) else within deploy_path do with fetch(:git_environmental_variables) do git_plugin.clone_repo end end end end end desc "Update the repo mirror to reflect the origin state" task update: :'git:clone' do on release_roles :all do within repo_path do with fetch(:git_environmental_variables) do git_plugin.update_mirror end end end end desc "Copy repo to releases" task create_release: :'git:update' do on release_roles :all do with fetch(:git_environmental_variables) do within repo_path do execute :mkdir, "-p", release_path git_plugin.archive_to_release_path end end end end desc "Determine the revision that will be deployed" task :set_current_revision do on release_roles :all do within repo_path do with fetch(:git_environmental_variables) do set :current_revision, git_plugin.fetch_revision end end end end end capistrano-3.10.0/lib/capistrano/application.rb0000644000004100000410000001002013175754131021527 0ustar www-datawww-datamodule Capistrano class Application < Rake::Application def initialize super @rakefiles = %w{capfile Capfile capfile.rb Capfile.rb} end def name "cap" end def run Rake.application = self super end def sort_options(options) not_applicable_to_capistrano = %w(quiet silent verbose) options.reject! do |(switch, *)| switch =~ /--#{Regexp.union(not_applicable_to_capistrano)}/ end super.push(version, dry_run, roles, hostfilter, print_config_variables) end def handle_options options.rakelib = ["rakelib"] options.trace_output = $stderr OptionParser.new do |opts| opts.banner = "See full documentation at http://capistranorb.com/." opts.separator "" opts.separator "Install capistrano in a project:" opts.separator " bundle exec cap install [STAGES=qa,staging,production,...]" opts.separator "" opts.separator "Show available tasks:" opts.separator " bundle exec cap -T" opts.separator "" opts.separator "Invoke (or simulate invoking) a task:" opts.separator " bundle exec cap [--dry-run] STAGE TASK" opts.separator "" opts.separator "Advanced options:" opts.on_tail("-h", "--help", "-H", "Display this help message.") do puts opts exit end standard_rake_options.each { |args| opts.on(*args) } opts.environment("RAKEOPT") end.parse! end def top_level_tasks if tasks_without_stage_dependency.include?(@top_level_tasks.first) @top_level_tasks else @top_level_tasks.unshift(ensure_stage.to_s) end end def display_error_message(ex) unless options.backtrace Rake.application.options.suppress_backtrace_pattern = backtrace_pattern if backtrace_pattern trace "(Backtrace restricted to imported tasks)" end super end def exit_because_of_exception(ex) if respond_to?(:deploying?) && deploying? exit_deploy_because_of_exception(ex) else super end end # allows the `cap install` task to load without a capfile def find_rakefile_location if (location = super).nil? [capfile, Dir.pwd] else location end end private def backtrace_pattern loc = Rake.application.find_rakefile_location return unless loc whitelist = (@imported.dup << loc[0]).map { |f| File.absolute_path(f, loc[1]) } /^(?!#{whitelist.map { |p| Regexp.quote(p) }.join('|')})/ end def load_imports if options.show_tasks && Rake::Task.task_defined?("load:defaults") invoke "load:defaults" set(:stage, "") Dir[deploy_config_path].each { |f| add_import f } end super end def capfile File.expand_path(File.join(File.dirname(__FILE__), "..", "Capfile")) end def version ["--version", "-V", "Display the program version.", lambda do |_value| puts "Capistrano Version: #{Capistrano::VERSION} (Rake Version: #{Rake::VERSION})" exit end] end def dry_run ["--dry-run", "-n", "Do a dry run without executing actions", lambda do |_value| Configuration.env.set(:sshkit_backend, SSHKit::Backend::Printer) end] end def roles ["--roles ROLES", "-r", "Run SSH commands only on hosts matching these roles", lambda do |value| Configuration.env.add_cmdline_filter(:role, value) end] end def hostfilter ["--hosts HOSTS", "-z", "Run SSH commands only on matching hosts", lambda do |value| Configuration.env.add_cmdline_filter(:host, value) end] end def print_config_variables ["--print-config-variables", "-p", "Display the defined config variables before starting the deployment tasks.", lambda do |_value| Configuration.env.set(:print_config_variables, true) end] end end end capistrano-3.10.0/lib/capistrano/scm.rb0000644000004100000410000000537213175754131020024 0ustar www-datawww-datamodule Capistrano # Base class for SCM strategy providers. # # @abstract # # @attr_reader [Rake] context # # @author Hartog de Mik # class SCM attr_reader :context # Provide a wrapper for the SCM that loads a strategy for the user. # # @param [Rake] context The context in which the strategy should run # @param [Module] strategy A module to include into the SCM instance. The # module should provide the abstract methods of Capistrano::SCM # def initialize(context, strategy) @context = context singleton = class << self; self; end singleton.send(:include, strategy) end # Call test in context def test!(*args) context.test(*args) end # The repository URL according to the context def repo_url context.repo_url end # The repository path according to the context def repo_path context.repo_path end # The release path according to the context def release_path context.release_path end # Fetch a var from the context # @param [Symbol] variable The variable to fetch # @param [Object] default The default value if not found # def fetch(*args) context.fetch(*args) end # @abstract # # Your implementation should check the existence of a cache repository on # the deployment target # # @return [Boolean] # def test raise NotImplementedError, "Your SCM strategy module should provide a #test method" end # @abstract # # Your implementation should check if the specified remote-repository is # available. # # @return [Boolean] # def check raise NotImplementedError, "Your SCM strategy module should provide a #check method" end # @abstract # # Create a (new) clone of the remote-repository on the deployment target # # @return void # def clone raise NotImplementedError, "Your SCM strategy module should provide a #clone method" end # @abstract # # Update the clone on the deployment target # # @return void # def update raise NotImplementedError, "Your SCM strategy module should provide a #update method" end # @abstract # # Copy the contents of the cache-repository onto the release path # # @return void # def release raise NotImplementedError, "Your SCM strategy module should provide a #release method" end # @abstract # # Identify the SHA of the commit that will be deployed. This will most likely involve SshKit's capture method. # # @return void # def fetch_revision raise NotImplementedError, "Your SCM strategy module should provide a #fetch_revision method" end end end capistrano-3.10.0/lib/capistrano/configuration.rb0000644000004100000410000001127013175754131022103 0ustar www-datawww-datarequire_relative "configuration/filter" require_relative "configuration/question" require_relative "configuration/plugin_installer" require_relative "configuration/server" require_relative "configuration/servers" require_relative "configuration/validated_variables" require_relative "configuration/variables" module Capistrano class ValidationError < RuntimeError; end class Configuration def self.env @env ||= new end def self.reset! @env = new end extend Forwardable attr_reader :variables def_delegators :variables, :set, :fetch, :fetch_for, :delete, :keys, :validate def initialize(values={}) @variables = ValidatedVariables.new(Variables.new(values)) end def ask(key, default=nil, options={}) question = Question.new(key, default, options) set(key, question) end def set_if_empty(key, value=nil, &block) set(key, value, &block) unless keys.include?(key) end def append(key, *values) set(key, Array(fetch(key)).concat(values)) end def remove(key, *values) set(key, Array(fetch(key)) - values) end def any?(key) value = fetch(key) if value && value.respond_to?(:any?) value.any? else !fetch(key).nil? end end def is_question?(key) value = fetch_for(key, nil) !value.nil? && value.is_a?(Question) end def role(name, hosts, options={}) if name == :all raise ArgumentError, "#{name} reserved name for role. Please choose another name" end servers.add_role(name, hosts, options) end def server(name, properties={}) servers.add_host(name, properties) end def roles_for(names) servers.roles_for(names) end def role_properties_for(names, &block) servers.role_properties_for(names, &block) end def primary(role) servers.fetch_primary(role) end def backend @backend ||= SSHKit end attr_writer :backend def configure_backend backend.configure do |sshkit| configure_sshkit_output(sshkit) sshkit.output_verbosity = fetch(:log_level) sshkit.default_env = fetch(:default_env) sshkit.backend = fetch(:sshkit_backend, SSHKit::Backend::Netssh) sshkit.backend.configure do |backend| backend.pty = fetch(:pty) backend.connection_timeout = fetch(:connection_timeout) backend.ssh_options = (backend.ssh_options || {}).merge(fetch(:ssh_options, {})) end end end def configure_scm Capistrano::Configuration::SCMResolver.new.resolve end def timestamp @timestamp ||= Time.now.utc end def add_filter(filter=nil, &block) if block raise ArgumentError, "Both a block and an object were given" if filter filter = Object.new def filter.filter(servers) block.call(servers) end elsif !filter.respond_to? :filter raise TypeError, "Provided custom filter <#{filter.inspect}> does " \ "not have a public 'filter' method" end @custom_filters ||= [] @custom_filters << filter end def setup_filters @filters = cmdline_filters @filters += @custom_filters if @custom_filters @filters << Filter.new(:role, ENV["ROLES"]) if ENV["ROLES"] @filters << Filter.new(:host, ENV["HOSTS"]) if ENV["HOSTS"] fh = fetch_for(:filter, {}) || {} @filters << Filter.new(:host, fh[:hosts]) if fh[:hosts] @filters << Filter.new(:role, fh[:roles]) if fh[:roles] @filters << Filter.new(:host, fh[:host]) if fh[:host] @filters << Filter.new(:role, fh[:role]) if fh[:role] end def add_cmdline_filter(type, values) cmdline_filters << Filter.new(type, values) end def filter(list) setup_filters if @filters.nil? @filters.reduce(list) { |l, f| f.filter l } end def dry_run? fetch(:sshkit_backend) == SSHKit::Backend::Printer end def install_plugin(plugin, load_hooks: true, load_immediately: false) installer.install(plugin, load_hooks: load_hooks, load_immediately: load_immediately) end def scm_plugin_installed? installer.scm_installed? end def servers @servers ||= Servers.new end private def cmdline_filters @cmdline_filters ||= [] end def installer @installer ||= PluginInstaller.new end def configure_sshkit_output(sshkit) format_args = [fetch(:format)] format_args.push(fetch(:format_options)) if any?(:format_options) sshkit.use_format(*format_args) end end end capistrano-3.10.0/lib/capistrano/upload_task.rb0000644000004100000410000000030213175754131021534 0ustar www-datawww-datarequire "rake/file_creation_task" module Capistrano class UploadTask < Rake::FileCreationTask def needed? true # always needed because we can't check remote hosts end end end capistrano-3.10.0/lib/capistrano/templates/0000755000004100000410000000000013175754131020704 5ustar www-datawww-datacapistrano-3.10.0/lib/capistrano/templates/Capfile0000644000004100000410000000211613175754131022172 0ustar www-datawww-data# Load DSL and set up stages require "capistrano/setup" # Include default deployment tasks require "capistrano/deploy" # Load the SCM plugin appropriate to your project: # # require "capistrano/scm/hg" # install_plugin Capistrano::SCM::Hg # or # require "capistrano/scm/svn" # install_plugin Capistrano::SCM::Svn # or require "capistrano/scm/git" install_plugin Capistrano::SCM::Git # Include tasks from other gems included in your Gemfile # # For documentation on these, see for example: # # https://github.com/capistrano/rvm # https://github.com/capistrano/rbenv # https://github.com/capistrano/chruby # https://github.com/capistrano/bundler # https://github.com/capistrano/rails # https://github.com/capistrano/passenger # # require "capistrano/rvm" # require "capistrano/rbenv" # require "capistrano/chruby" # require "capistrano/bundler" # require "capistrano/rails/assets" # require "capistrano/rails/migrations" # require "capistrano/passenger" # Load custom tasks from `lib/capistrano/tasks` if you have any defined Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r } capistrano-3.10.0/lib/capistrano/templates/stage.rb.erb0000644000004100000410000000405013175754131023102 0ustar www-datawww-data# server-based syntax # ====================== # Defines a single server with a list of roles and multiple properties. # You can define all roles on a single server, or split them: # server "example.com", user: "deploy", roles: %w{app db web}, my_property: :my_value # server "example.com", user: "deploy", roles: %w{app web}, other_property: :other_value # server "db.example.com", user: "deploy", roles: %w{db} # role-based syntax # ================== # Defines a role with one or multiple servers. The primary server in each # group is considered to be the first unless any hosts have the primary # property set. Specify the username and a domain or IP for the server. # Don't use `:all`, it's a meta role. # role :app, %w{deploy@example.com}, my_property: :my_value # role :web, %w{user1@primary.com user2@additional.com}, other_property: :other_value # role :db, %w{deploy@example.com} # Configuration # ============= # You can set any configuration variable like in config/deploy.rb # These variables are then only loaded and set in this stage. # For available Capistrano configuration variables see the documentation page. # http://capistranorb.com/documentation/getting-started/configuration/ # Feel free to add new variables to customise your setup. # Custom SSH Options # ================== # You may pass any option but keep in mind that net/ssh understands a # limited set of options, consult the Net::SSH documentation. # http://net-ssh.github.io/net-ssh/classes/Net/SSH.html#method-c-start # # Global options # -------------- # set :ssh_options, { # keys: %w(/home/rlisowski/.ssh/id_rsa), # forward_agent: false, # auth_methods: %w(password) # } # # The server-based syntax can be used to override options: # ------------------------------------ # server "example.com", # user: "user_name", # roles: %w{web app}, # ssh_options: { # user: "user_name", # overrides user setting above # keys: %w(/home/user_name/.ssh/id_rsa), # forward_agent: false, # auth_methods: %w(publickey password) # # password: "please use keys" # } capistrano-3.10.0/lib/capistrano/templates/deploy.rb.erb0000644000004100000410000000244113175754131023275 0ustar www-datawww-data# config valid for current version and patch releases of Capistrano lock "~> <%= Capistrano::VERSION %>" set :application, "my_app_name" set :repo_url, "git@example.com:me/my_repo.git" # Default branch is :master # ask :branch, `git rev-parse --abbrev-ref HEAD`.chomp # Default deploy_to directory is /var/www/my_app_name # set :deploy_to, "/var/www/my_app_name" # Default value for :format is :airbrussh. # set :format, :airbrussh # You can configure the Airbrussh format using :format_options. # These are the defaults. # set :format_options, command_output: true, log_file: "log/capistrano.log", color: :auto, truncate: :auto # Default value for :pty is false # set :pty, true # Default value for :linked_files is [] # append :linked_files, "config/database.yml", "config/secrets.yml" # Default value for linked_dirs is [] # append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "public/system" # Default value for default_env is {} # set :default_env, { path: "/opt/ruby/bin:$PATH" } # Default value for local_user is ENV['USER'] # set :local_user, -> { `git config user.name`.chomp } # Default value for keep_releases is 5 # set :keep_releases, 5 # Uncomment the following to require manually verifying the host key before first deploy. # set :ssh_options, verify_host_key: :secure capistrano-3.10.0/lib/capistrano/plugin.rb0000644000004100000410000000555513175754131020543 0ustar www-datawww-datarequire "capistrano/all" require "rake/tasklib" # IMPORTANT: The Capistrano::Plugin system is not yet considered a stable, # public API, and is subject to change without notice. Eventually it will be # officially documented and supported, but for now, use it at your own risk. # # Base class for Capistrano plugins. Makes building a Capistrano plugin as easy # as writing a `Capistrano::Plugin` subclass and overriding any or all of its # three template methods: # # * set_defaults # * register_hooks # * define_tasks # # Within the plugin you can use any methods of the Rake or Capistrano DSLs, like # `fetch`, `invoke`, etc. In cases when you need to use SSHKit's backend outside # of an `on` block, use the `backend` convenience method. E.g. `backend.test`, # `backend.execute`, or `backend.capture`. # # Package up and distribute your plugin class as a gem and you're good to go! # # To use a plugin, all a user has to do is install it in the Capfile, like this: # # # Capfile # require "capistrano/superfancy" # install_plugin Capistrano::Superfancy # # Or, to install the plugin without its hooks: # # # Capfile # require "capistrano/superfancy" # install_plugin Capistrano::Superfancy, load_hooks: false # class Capistrano::Plugin < Rake::TaskLib include Capistrano::DSL # Implemented by subclasses to provide default values for settings needed by # this plugin. Typically done using the `set_if_empty` Capistrano DSL method. # # Example: # # def set_defaults # set_if_empty :my_plugin_option, true # end # def set_defaults; end # Implemented by subclasses to hook into Capistrano's deployment flow using # using the `before` and `after` DSL methods. Note that `register_hooks` will # not be called if the user has opted-out of hooks when installing the plugin. # # Example: # # def register_hooks # after "deploy:updated", "my_plugin:do_something" # end # def register_hooks; end # Implemented by subclasses to define Rake tasks. Typically a plugin will call # `eval_rakefile` to load Rake tasks from a separate .rake file. # # Example: # # def define_tasks # eval_rakefile File.expand_path("../tasks.rake", __FILE__) # end # # For simple tasks, you can define them inline. No need for a separate file. # # def define_tasks # desc "Do something fantastic." # task "my_plugin:fantastic" do # ... # end # end # def define_tasks; end private # Read and eval a .rake file in such a way that `self` within the .rake file # refers to this plugin instance. This gives the tasks in the file access to # helper methods defined by the plugin. def eval_rakefile(path) contents = IO.read(path) instance_eval(contents, path, 1) end # Convenience to access the current SSHKit backend outside of an `on` block. def backend SSHKit::Backend.current end end capistrano-3.10.0/lib/capistrano/console.rb0000644000004100000410000000007113175754131020673 0ustar www-datawww-dataload File.expand_path("../tasks/console.rake", __FILE__) capistrano-3.10.0/lib/capistrano/defaults.rb0000644000004100000410000000232313175754131021042 0ustar www-datawww-datavalidate :application do |_key, value| changed_value = value.gsub(/[^A-Z0-9\.\-]/i, "_") if value != changed_value warn %Q(The :application value "#{value}" is invalid!) warn "Use only letters, numbers, hyphens, dots, and underscores. For example:" warn " set :application, '#{changed_value}'" raise Capistrano::ValidationError end end %i(git_strategy hg_strategy svn_strategy).each do |strategy| validate(strategy) do |key, _value| warn( "[Deprecation Warning] #{key} is deprecated and will be removed in "\ "Capistrano 3.7.0.\n"\ "https://github.com/capistrano/capistrano/blob/master/UPGRADING-3.7.md" ) end end # We use a special :_default_git value so that SCMResolver can tell whether the # default has been replaced by the user via `set`. set_if_empty :scm, Capistrano::Configuration::SCMResolver::DEFAULT_GIT set_if_empty :branch, "master" set_if_empty :deploy_to, -> { "/var/www/#{fetch(:application)}" } set_if_empty :tmp_dir, "/tmp" set_if_empty :default_env, {} set_if_empty :keep_releases, 5 set_if_empty :format, :airbrussh set_if_empty :log_level, :debug set_if_empty :pty, false set_if_empty :local_user, -> { ENV["USER"] || ENV["LOGNAME"] || ENV["USERNAME"] } capistrano-3.10.0/lib/capistrano/configuration/0000755000004100000410000000000013175754131021555 5ustar www-datawww-datacapistrano-3.10.0/lib/capistrano/configuration/null_filter.rb0000644000004100000410000000020313175754131024414 0ustar www-datawww-datamodule Capistrano class Configuration class NullFilter def filter(servers) servers end end end end capistrano-3.10.0/lib/capistrano/configuration/scm_resolver.rb0000644000004100000410000001116713175754131024613 0ustar www-datawww-datamodule Capistrano class Configuration # In earlier versions of Capistrano, users would specify the desired SCM # implementation using `set :scm, :git`, for example. Capistrano would then # load the matching .rb file based on this variable. # # Now we expect users to explicitly `require` and call `new` on the desired # SCM implementation in their Capfile. The `set` technique is deprecated. # # This SCMResolver class takes care of managing the transition from the old # to new system. It maintains the legacy behavior, but prints deprecation # warnings when it is used. # # To maintain backwards compatibility, the resolver will load the Git SCM by # if default it determines that no SCM has been explicitly specified or # loaded. To force no SCM to be used at all, use `set :scm, nil`. This hack # won't be necessary once backwards compatibility is removed in a future # version. # # TODO: Remove this class entirely in Capistrano 4.0. # class SCMResolver DEFAULT_GIT = :"default-git" include Capistrano::DSL def resolve return if scm_name.nil? set(:scm, :git) if using_default_scm? print_deprecation_warnings_if_applicable # Note that `scm_plugin_installed?` comes from Capistrano::DSL if scm_plugin_installed? delete(:scm) return end if built_in_scm_name? load_built_in_scm else # Compatibility with existing 3.x third-party SCMs register_legacy_scm_hooks load_legacy_scm_by_name end end private def using_default_scm? return @using_default_scm if defined? @using_default_scm @using_default_scm = (fetch(:scm) == DEFAULT_GIT) end def scm_name fetch(:scm) end def load_built_in_scm require "capistrano/scm/#{scm_name}" scm_class = Object.const_get(built_in_scm_plugin_class_name) # We use :load_immediately because we are initializing the SCM plugin # late in the load process and therefore can't use the standard # load:defaults technique. install_plugin(scm_class, load_immediately: true) end def load_legacy_scm_by_name load("capistrano/#{scm_name}.rb") end def third_party_scm_name? !built_in_scm_name? end def built_in_scm_name? %w(git hg svn).include?(scm_name.to_s.downcase) end def built_in_scm_plugin_class_name "Capistrano::SCM::#{scm_name.to_s.capitalize}" end # rubocop:disable Style/GuardClause def register_legacy_scm_hooks if Rake::Task.task_defined?("deploy:new_release_path") after "deploy:new_release_path", "#{scm_name}:create_release" end if Rake::Task.task_defined?("deploy:check") before "deploy:check", "#{scm_name}:check" end if Rake::Task.task_defined?("deploy:set_current_revision") before "deploy:set_current_revision", "#{scm_name}:set_current_revision" end end # rubocop:enable Style/GuardClause def print_deprecation_warnings_if_applicable if using_default_scm? warn_add_git_to_capfile unless scm_plugin_installed? elsif built_in_scm_name? warn_set_scm_is_deprecated elsif third_party_scm_name? warn_third_party_scm_must_be_upgraded end end def warn_set_scm_is_deprecated $stderr.puts(<<-MESSAGE) [Deprecation Notice] `set :scm, #{scm_name.inspect}` is deprecated. To ensure your project is compatible with future versions of Capistrano, remove the :scm setting and instead add these lines to your Capfile after `require "capistrano/deploy"`: require "capistrano/scm/#{scm_name}" install_plugin #{built_in_scm_plugin_class_name} MESSAGE end def warn_add_git_to_capfile $stderr.puts(<<-MESSAGE) [Deprecation Notice] Future versions of Capistrano will not load the Git SCM plugin by default. To silence this deprecation warning, add the following to your Capfile after `require "capistrano/deploy"`: require "capistrano/scm/git" install_plugin Capistrano::SCM::Git MESSAGE end def warn_third_party_scm_must_be_upgraded $stderr.puts(<<-MESSAGE) [Deprecation Notice] `set :scm, #{scm_name.inspect}` is deprecated. To ensure this custom SCM will work with future versions of Capistrano, please upgrade it to a version that uses the new SCM plugin mechanism documented here: http://capistranorb.com/documentation/advanced-features/custom-scm MESSAGE end end end end capistrano-3.10.0/lib/capistrano/configuration/empty_filter.rb0000644000004100000410000000020013175754131024575 0ustar www-datawww-datamodule Capistrano class Configuration class EmptyFilter def filter(_servers) [] end end end end capistrano-3.10.0/lib/capistrano/configuration/servers.rb0000644000004100000410000000422613175754131023577 0ustar www-datawww-datarequire "set" require "capistrano/configuration" require "capistrano/configuration/filter" module Capistrano class Configuration class Servers include Enumerable def add_host(host, properties={}) new_host = Server[host] new_host.port = properties[:port] if properties.key?(:port) # This matching logic must stay in sync with `Server#matches?`. key = ServerKey.new(new_host.hostname, new_host.port) existing = servers_by_key[key] if existing existing.user = new_host.user if new_host.user existing.with(properties) else servers_by_key[key] = new_host.with(properties) end end # rubocop:disable Security/MarshalLoad def add_role(role, hosts, options={}) options_deepcopy = Marshal.dump(options.merge(roles: role)) Array(hosts).each { |host| add_host(host, Marshal.load(options_deepcopy)) } end # rubocop:enable Security/MarshalLoad def roles_for(names) options = extract_options(names) s = Filter.new(:role, names).filter(servers_by_key.values) s.select { |server| server.select?(options) } end def role_properties_for(rolenames) roles = rolenames.to_set rps = Set.new unless block_given? roles_for(rolenames).each do |host| host.roles.intersection(roles).each do |role| [host.properties.fetch(role)].flatten(1).each do |props| if block_given? yield host, role, props else rps << (props || {}).merge(role: role, hostname: host.hostname) end end end end block_given? ? nil : rps end def fetch_primary(role) hosts = roles_for([role]) hosts.find(&:primary) || hosts.first end def each servers_by_key.values.each { |server| yield server } end private ServerKey = Struct.new(:hostname, :port) def servers_by_key @servers_by_key ||= {} end def extract_options(array) array.last.is_a?(::Hash) ? array.pop : {} end end end end capistrano-3.10.0/lib/capistrano/configuration/variables.rb0000644000004100000410000000554013175754131024056 0ustar www-datawww-datarequire "capistrano/proc_helpers" module Capistrano class Configuration # Holds the variables assigned at Capistrano runtime via `set` and retrieved # with `fetch`. Does internal bookkeeping to help identify user mistakes # like spelling errors or unused variables that may lead to unexpected # behavior. class Variables CAPISTRANO_LOCATION = File.expand_path("../..", __FILE__).freeze IGNORED_LOCATIONS = [ "#{CAPISTRANO_LOCATION}/configuration/variables.rb:", "#{CAPISTRANO_LOCATION}/configuration.rb:", "#{CAPISTRANO_LOCATION}/dsl/env.rb:", "/dsl.rb:", "/forwardable.rb:" ].freeze private_constant :CAPISTRANO_LOCATION, :IGNORED_LOCATIONS include Capistrano::ProcHelpers def initialize(values={}) @trusted_keys = [] @fetched_keys = [] @locations = {} @values = values @trusted = true end def untrusted! @trusted = false yield ensure @trusted = true end def set(key, value=nil, &block) @trusted_keys << key if trusted? && !@trusted_keys.include?(key) remember_location(key) values[key] = block || value trace_set(key) values[key] end def fetch(key, default=nil, &block) fetched_keys << key unless fetched_keys.include?(key) peek(key, default, &block) end # Internal use only. def peek(key, default=nil, &block) value = fetch_for(key, default, &block) while callable_without_parameters?(value) value = (values[key] = value.call) end value end def fetch_for(key, default, &block) block ? values.fetch(key, &block) : values.fetch(key, default) end def delete(key) values.delete(key) end def trusted_keys @trusted_keys.dup end def untrusted_keys keys - @trusted_keys end def keys values.keys end # Keys that have been set, but which have never been fetched. def unused_keys keys - fetched_keys end # Returns an array of source file location(s) where the given key was # assigned (i.e. where `set` was called). If the key was never assigned, # returns `nil`. def source_locations(key) locations[key] end private attr_reader :locations, :values, :fetched_keys def trusted? @trusted end def remember_location(key) location = caller.find do |line| IGNORED_LOCATIONS.none? { |i| line.include?(i) } end (locations[key] ||= []) << location end def trace_set(key) return unless fetch(:print_config_variables, false) puts "Config variable set: #{key.inspect} => #{values[key].inspect}" end end end end capistrano-3.10.0/lib/capistrano/configuration/host_filter.rb0000644000004100000410000000126713175754131024432 0ustar www-datawww-datamodule Capistrano class Configuration class HostFilter def initialize(values) av = Array(values).dup av = av.flat_map { |v| v.is_a?(String) && v =~ /^(?[-A-Za-z1-9.]+)(,\g)*$/ ? v.split(",") : v } @rex = regex_matcher(av) end def filter(servers) Array(servers).select { |s| @rex.match s.to_s } end private def regex_matcher(values) values.map! do |v| case v when Regexp then v else vs = v.to_s vs =~ /^[-A-Za-z0-9.]+$/ ? /^#{Regexp.quote(vs)}$/ : Regexp.new(vs) end end Regexp.union values end end end end capistrano-3.10.0/lib/capistrano/configuration/filter.rb0000644000004100000410000000154113175754131023370 0ustar www-datawww-datarequire "capistrano/configuration" require "capistrano/configuration/empty_filter" require "capistrano/configuration/host_filter" require "capistrano/configuration/null_filter" require "capistrano/configuration/role_filter" module Capistrano class Configuration class Filter def initialize(type, values=nil) raise "Invalid filter type #{type}" unless %i(host role).include? type av = Array(values) @strategy = if av.empty? then EmptyFilter.new elsif av.include?(:all) || av.include?("all") then NullFilter.new elsif type == :host then HostFilter.new(values) elsif type == :role then RoleFilter.new(values) else NullFilter.new end end def filter(servers) @strategy.filter servers end end end end capistrano-3.10.0/lib/capistrano/configuration/validated_variables.rb0000644000004100000410000000671013175754131026073 0ustar www-datawww-datarequire "capistrano/proc_helpers" require "delegate" module Capistrano class Configuration # Decorates a Variables object to additionally perform an optional set of # user-supplied validation rules. Each rule for a given key is invoked # immediately whenever `set` is called with a value for that key. # # If `set` is called with a callable value or a block, validation is not # performed immediately. Instead, the validation rules are invoked the first # time `fetch` is used to access the value. # # A rule is simply a block that accepts two arguments: key and value. It is # up to the rule to raise an exception when it deems the value is invalid # (or just print a warning). # # Rules can be registered using the DSL like this: # # validate(:my_key) do |key, value| # # rule goes here # end # class ValidatedVariables < SimpleDelegator include Capistrano::ProcHelpers def initialize(variables) super(variables) @validators = {} end # Decorate Variables#set to add validation behavior. def set(key, value=nil, &block) assert_value_or_block_not_both(value, block) # Skip validation behavior if no validators are registered for this key return super unless validators.key?(key) value_to_evaluate = block || value if callable_without_parameters?(value_to_evaluate) super(key, assert_valid_later(key, value_to_evaluate), &nil) else assert_valid_now(key, value_to_evaluate) super end end # Register a validation rule for the given key. def validate(key, &validator) vs = (validators[key] || []) vs << validator validators[key] = vs end private attr_reader :validators # Given a callable that provides a value, wrap the callable with another # object that responds to `call`. This new object will perform validation # and then return the original callable's value. # # If the callable is a `Question`, the object returned by this method will # also be a `Question` (a `ValidatedQuestion`, to be precise). This # ensures that `is_a?(Question)` remains true even after the validation # wrapper is applied. This is needed so that `Configuration#is_question?` # works as expected. # def assert_valid_later(key, callable) validation_callback = lambda do value = callable.call assert_valid_now(key, value) value end if callable.is_a?(Question) ValidatedQuestion.new(validation_callback) else validation_callback end end # Runs all validation rules registered for the given key against the # user-supplied value for that variable. If no validator raises an # exception, the value is assumed to be valid. def assert_valid_now(key, value) validators[key].each do |validator| validator.call(key, value) end end def assert_value_or_block_not_both(value, block) return if value.nil? || block.nil? raise Capistrano::ValidationError, "Value and block both passed to Configuration#set" end class ValidatedQuestion < Question def initialize(validator) @validator = validator end def call @validator.call end end end end end capistrano-3.10.0/lib/capistrano/configuration/role_filter.rb0000644000004100000410000000125313175754131024411 0ustar www-datawww-datamodule Capistrano class Configuration class RoleFilter def initialize(values) av = Array(values).dup av = av.flat_map { |v| v.is_a?(String) ? v.split(",") : v } @rex = regex_matcher(av) end def filter(servers) Array(servers).select { |s| s.is_a?(String) ? false : s.roles.any? { |r| @rex.match r } } end private def regex_matcher(values) values.map! do |v| case v when Regexp then v else vs = v.to_s vs =~ %r{^/(.+)/$} ? Regexp.new($1) : /^#{Regexp.quote(vs)}$/ end end Regexp.union values end end end end capistrano-3.10.0/lib/capistrano/configuration/question.rb0000644000004100000410000000224313175754131023752 0ustar www-datawww-datamodule Capistrano class Configuration class Question def initialize(key, default, options={}) @key = key @default = default @options = options end def call ask_question value_or_default end private attr_reader :key, :default, :options def ask_question $stdout.print question $stdout.flush end def value_or_default if response.empty? default else response end end def response return @response if defined? @response @response = (gets || "").chomp end def gets if echo? $stdin.gets else $stdin.noecho(&:gets).tap { $stdout.print "\n" } end rescue Errno::EIO # when stdio gets closed return end def question if default.nil? I18n.t(:question, key: key, scope: :capistrano) else I18n.t(:question_default, key: key, default_value: default, scope: :capistrano) end end def echo? (options || {}).fetch(:echo, true) end end end end capistrano-3.10.0/lib/capistrano/configuration/plugin_installer.rb0000644000004100000410000000311413175754131025454 0ustar www-datawww-data# Encapsulates the logic for installing plugins into Capistrano. Plugins must # simply conform to a basic API; the PluginInstaller takes care of invoking the # API at appropriate times. # # This class is not used directly; instead it is typically accessed via the # `install_plugin` method of the Capistrano DSL. # module Capistrano class Configuration class PluginInstaller # "Installs" a Plugin into Capistrano by loading its tasks, hooks, and # defaults at the appropriate time. The hooks in particular can be # skipped, if you want full control over when and how the plugin's tasks # are executed. Simply pass `load_hooks:false` to opt out. # # The plugin class or instance may be provided. These are equivalent: # # install(Capistrano::SCM::Git) # install(Capistrano::SCM::Git.new) # # Note that the :load_immediately flag is for internal use only and will # be removed in an upcoming release. # def install(plugin, load_hooks: true, load_immediately: false) plugin = plugin.is_a?(Class) ? plugin.new : plugin plugin.define_tasks plugin.register_hooks if load_hooks @scm_installed ||= provides_scm?(plugin) if load_immediately plugin.set_defaults else Rake::Task.define_task("load:defaults") do plugin.set_defaults end end end def scm_installed? @scm_installed end private def provides_scm?(plugin) plugin.respond_to?(:scm?) && plugin.scm? end end end end capistrano-3.10.0/lib/capistrano/configuration/server.rb0000644000004100000410000000555013175754131023415 0ustar www-datawww-datarequire "set" module Capistrano class Configuration class Server < SSHKit::Host extend Forwardable def_delegators :properties, :roles, :fetch, :set def self.[](host) host.is_a?(Server) ? host : new(host) end def add_roles(roles) Array(roles).each { |role| add_role(role) } self end alias roles= add_roles def add_role(role) roles.add role.to_sym self end def has_role?(role) roles.include? role.to_sym end def select?(options) options.each do |k, v| callable = v.respond_to?(:call) ? v : ->(server) { server.fetch(v) } result = \ case k when :filter, :select callable.call(self) when :exclude !callable.call(self) else fetch(k) == v end return false unless result end true end def primary self if fetch(:primary) end def with(properties) properties.each { |key, value| add_property(key, value) } self end def properties @properties ||= Properties.new end def netssh_options @netssh_options ||= super.merge(fetch(:ssh_options) || {}) end def roles_array roles.to_a end def matches?(other) # This matching logic must stay in sync with `Servers#add_host`. hostname == other.hostname && port == other.port end private def add_property(key, value) if respond_to?("#{key}=") send("#{key}=", value) else set(key, value) end end class Properties def initialize @properties = {} end def set(key, value) pval = @properties[key] if pval.is_a?(Hash) && value.is_a?(Hash) pval.merge!(value) elsif pval.is_a?(Set) && value.is_a?(Set) pval.merge(value) elsif pval.is_a?(Array) && value.is_a?(Array) pval.concat value else @properties[key] = value end end def fetch(key) @properties[key] end def respond_to_missing?(method, _include_all=false) @properties.key?(method) || super end def roles @roles ||= Set.new end def keys @properties.keys end # rubocop:disable Style/MethodMissing def method_missing(key, value=nil) if value set(lvalue(key), value) else fetch(key) end end # rubocop:enable Style/MethodMissing def to_h @properties end private def lvalue(key) key.to_s.chomp("=").to_sym end end end end end capistrano-3.10.0/lib/capistrano/doctor.rb0000644000004100000410000000035013175754131020523 0ustar www-datawww-datarequire "capistrano/doctor/environment_doctor" require "capistrano/doctor/gems_doctor" require "capistrano/doctor/variables_doctor" require "capistrano/doctor/servers_doctor" load File.expand_path("../tasks/doctor.rake", __FILE__) capistrano-3.10.0/lib/capistrano/install.rb0000644000004100000410000000011713175754131020700 0ustar www-datawww-dataload File.expand_path(File.join(File.dirname(__FILE__), "tasks/install.rake")) capistrano-3.10.0/lib/capistrano/version.rb0000644000004100000410000000006213175754131020716 0ustar www-datawww-datamodule Capistrano VERSION = "3.10.0".freeze end capistrano-3.10.0/lib/capistrano/immutable_task.rb0000644000004100000410000000170413175754131022236 0ustar www-datawww-datamodule Capistrano # This module extends a Rake::Task to freeze it to prevent it from being # enhanced. This is used to prevent users from enhancing a task at the wrong # point of Capistrano's boot process, which can happen if a Capistrano plugin # is loaded in deploy.rb by mistake (instead of in the Capfile). # # Usage: # # task = Rake.application["load:defaults"] # task.invoke # task.extend(Capistrano::ImmutableTask) # prevent further modifications # module ImmutableTask def self.extended(task) task.freeze end def enhance(*args, &block) $stderr.puts <<-MESSAGE ERROR: #{name} has already been invoked and can no longer be modified. Check that you haven't loaded a Capistrano plugin in deploy.rb or a stage (e.g. deploy/production.rb) by mistake. Plugins must be loaded in the Capfile to initialize properly. MESSAGE # This will raise a frozen object error super(*args, &block) end end end capistrano-3.10.0/lib/capistrano/i18n.rb0000644000004100000410000000367413175754131020024 0ustar www-datawww-datarequire "i18n" en = { starting: "Starting", capified: "Capified", start: "Start", update: "Update", finalize: "Finalise", finishing: "Finishing", finished: "Finished", stage_not_set: "Stage not set, please call something such as `cap production deploy`, where production is a stage you have defined.", written_file: "create %{file}", question: "Please enter %{key}: ", question_default: "Please enter %{key} (%{default_value}): ", keeping_releases: "Keeping %{keep_releases} of %{releases} deployed releases on %{host}", skip_cleanup: "Skipping cleanup of invalid releases on %{host}; unexpected foldername found (should be timestamp)", wont_delete_current_release: "Current release was marked for being removed but it's going to be skipped on %{host}", no_current_release: "There is no current release present on %{host}", no_old_releases: "No old releases (keeping newest %{keep_releases}) on %{host}", linked_file_does_not_exist: "linked file %{file} does not exist on %{host}", cannot_rollback: "There are no older releases to rollback to", cannot_found_rollback_release: "Cannot rollback because release %{release} does not exist", mirror_exists: "The repository mirror is at %{at}", revision_log_message: "Branch %{branch} (at %{sha}) deployed as release %{release} by %{user}", rollback_log_message: "%{user} rolled back to release %{release}", deploy_failed: "The deploy has failed with an error: %{ex}", console: { welcome: "capistrano console - enter command to execute on %{stage}", bye: "bye" }, error: { invalid_stage_name: '"%{name}" is a reserved word and cannot be used as a stage. Rename "%{path}" to something else.', user: { does_not_exist: "User %{user} does not exists", cannot_switch: "Cannot switch to user %{user}" } } } I18n.backend.store_translations(:en, capistrano: en) if I18n.respond_to?(:enforce_available_locales=) I18n.enforce_available_locales = true end capistrano-3.10.0/lib/capistrano/dsl/0000755000004100000410000000000013175754131017470 5ustar www-datawww-datacapistrano-3.10.0/lib/capistrano/dsl/stages.rb0000644000004100000410000000133213175754131021302 0ustar www-datawww-datamodule Capistrano module DSL module Stages RESERVED_NAMES = %w(deploy doctor install).freeze private_constant :RESERVED_NAMES def stages names = Dir[stage_definitions].map { |f| File.basename(f, ".rb") } assert_valid_stage_names(names) names end def stage_definitions stage_config_path.join("*.rb") end def stage_set? !!fetch(:stage, false) end private def assert_valid_stage_names(names) invalid = names.find { |n| RESERVED_NAMES.include?(n) } return if invalid.nil? raise t("error.invalid_stage_name", name: invalid, path: stage_config_path.join("#{invalid}.rb")) end end end end capistrano-3.10.0/lib/capistrano/dsl/task_enhancements.rb0000644000004100000410000000306013175754131023506 0ustar www-datawww-datarequire "capistrano/upload_task" module Capistrano module TaskEnhancements def before(task, prerequisite, *args, &block) prerequisite = Rake::Task.define_task(prerequisite, *args, &block) if block_given? Rake::Task[task].enhance [prerequisite] end def after(task, post_task, *args, &block) Rake::Task.define_task(post_task, *args, &block) if block_given? task = Rake::Task[task] task.enhance do post = Rake.application.lookup(post_task, task.scope) raise ArgumentError, "Task #{post_task.inspect} not found" unless post post.invoke end end def define_remote_file_task(task, target_roles) Capistrano::UploadTask.define_task(task) do |t| prerequisite_file = t.prerequisites.first file = shared_path.join(t.name) on roles(target_roles) do unless test "[ -f #{file.to_s.shellescape} ]" info "Uploading #{prerequisite_file} to #{file}" upload! File.open(prerequisite_file), file end end end end def ensure_stage Rake::Task.define_task(:ensure_stage) do unless stage_set? puts t(:stage_not_set) exit 1 end end end def tasks_without_stage_dependency stages + default_tasks end def default_tasks %w{install} end def exit_deploy_because_of_exception(ex) warn t(:deploy_failed, ex: ex.message) invoke "deploy:failed" exit(false) end def deploying? fetch(:deploying, false) end end end capistrano-3.10.0/lib/capistrano/dsl/paths.rb0000644000004100000410000000356413175754131021144 0ustar www-datawww-datarequire "pathname" module Capistrano module DSL module Paths def deploy_to fetch(:deploy_to) end def deploy_path Pathname.new(deploy_to) end def current_path deploy_path.join(fetch(:current_directory, "current")) end def releases_path deploy_path.join(fetch(:releases_directory, "releases")) end def release_path fetch(:release_path) { current_path } end def set_release_path(timestamp=now) set(:release_timestamp, timestamp) set(:release_path, releases_path.join(timestamp)) end def stage_config_path Pathname.new fetch(:stage_config_path, "config/deploy") end def deploy_config_path Pathname.new fetch(:deploy_config_path, "config/deploy.rb") end def repo_url fetch(:repo_url) end def repo_path Pathname.new(fetch(:repo_path, ->() { deploy_path.join("repo") })) end def shared_path deploy_path.join(fetch(:shared_directory, "shared")) end def revision_log deploy_path.join("revisions.log") end def now env.timestamp.strftime("%Y%m%d%H%M%S") end def asset_timestamp env.timestamp.strftime("%Y%m%d%H%M.%S") end def linked_dirs(parent) paths = fetch(:linked_dirs) join_paths(parent, paths) end def linked_files(parent) paths = fetch(:linked_files) join_paths(parent, paths) end def linked_file_dirs(parent) map_dirnames(linked_files(parent)) end def linked_dir_parents(parent) map_dirnames(linked_dirs(parent)) end def join_paths(parent, paths) paths.map { |path| parent.join(path) } end def map_dirnames(paths) paths.map(&:dirname).uniq end end end end capistrano-3.10.0/lib/capistrano/dsl/env.rb0000644000004100000410000000201513175754131020603 0ustar www-datawww-datarequire "forwardable" module Capistrano module DSL module Env extend Forwardable def_delegators :env, :configure_backend, :fetch, :set, :set_if_empty, :delete, :ask, :role, :server, :primary, :validate, :append, :remove, :dry_run?, :install_plugin, :any?, :is_question?, :configure_scm, :scm_plugin_installed? def roles(*names) env.roles_for(names.flatten) end def role_properties(*names, &block) env.role_properties_for(names, &block) end def release_roles(*names) if names.last.is_a? Hash names.last[:exclude] = :no_release else names << { exclude: :no_release } end roles(*names) end def env Configuration.env end def release_timestamp env.timestamp.strftime("%Y%m%d%H%M%S") end def asset_timestamp env.timestamp.strftime("%Y%m%d%H%M.%S") end end end end capistrano-3.10.0/lib/capistrano/all.rb0000644000004100000410000000054013175754131020002 0ustar www-datawww-datarequire "rake" require "sshkit" require "io/console" Rake.application.options.trace = true require "capistrano/version" require "capistrano/version_validator" require "capistrano/i18n" require "capistrano/dsl" require "capistrano/application" require "capistrano/configuration" require "capistrano/configuration/scm_resolver" module Capistrano end capistrano-3.10.0/lib/capistrano/tasks/0000755000004100000410000000000013175754131020033 5ustar www-datawww-datacapistrano-3.10.0/lib/capistrano/tasks/doctor.rake0000644000004100000410000000124313175754131022171 0ustar www-datawww-datadesc "Display a Capistrano troubleshooting report (all doctor: tasks)" task doctor: ["doctor:environment", "doctor:gems", "doctor:variables", "doctor:servers"] namespace :doctor do desc "Display Ruby environment details" task :environment do Capistrano::Doctor::EnvironmentDoctor.new.call end desc "Display Capistrano gem versions" task :gems do Capistrano::Doctor::GemsDoctor.new.call end desc "Display the values of all Capistrano variables" task :variables do Capistrano::Doctor::VariablesDoctor.new.call end desc "Display the effective servers configuration" task :servers do Capistrano::Doctor::ServersDoctor.new.call end end capistrano-3.10.0/lib/capistrano/tasks/install.rake0000644000004100000410000000242313175754131022346 0ustar www-datawww-datarequire "erb" require "pathname" desc "Install Capistrano, cap install STAGES=staging,production" task :install do envs = ENV["STAGES"] || "staging,production" tasks_dir = Pathname.new("lib/capistrano/tasks") config_dir = Pathname.new("config") deploy_dir = config_dir.join("deploy") deploy_rb = File.expand_path("../../templates/deploy.rb.erb", __FILE__) stage_rb = File.expand_path("../../templates/stage.rb.erb", __FILE__) capfile = File.expand_path("../../templates/Capfile", __FILE__) mkdir_p deploy_dir entries = [{ template: deploy_rb, file: config_dir.join("deploy.rb") }] entries += envs.split(",").map { |stage| { template: stage_rb, file: deploy_dir.join("#{stage}.rb") } } entries.each do |entry| if File.exist?(entry[:file]) warn "[skip] #{entry[:file]} already exists" else File.open(entry[:file], "w+") do |f| f.write(ERB.new(File.read(entry[:template])).result(binding)) puts I18n.t(:written_file, scope: :capistrano, file: entry[:file]) end end end mkdir_p tasks_dir if File.exist?("Capfile") warn "[skip] Capfile already exists" else FileUtils.cp(capfile, "Capfile") puts I18n.t(:written_file, scope: :capistrano, file: "Capfile") end puts I18n.t :capified, scope: :capistrano end capistrano-3.10.0/lib/capistrano/tasks/framework.rake0000644000004100000410000000225413175754131022677 0ustar www-datawww-datanamespace :deploy do desc "Start a deployment, make sure server(s) ready." task :starting do end desc "Started" task :started do end desc "Update server(s) by setting up a new release." task :updating do end desc "Updated" task :updated do end desc "Revert server(s) to previous release." task :reverting do end desc "Reverted" task :reverted do end desc "Publish the release." task :publishing do end desc "Published" task :published do end desc "Finish the deployment, clean up server(s)." task :finishing do end desc "Finish the rollback, clean up server(s)." task :finishing_rollback do end desc "Finished" task :finished do end desc "Rollback to previous release." task :rollback do %w{ starting started reverting reverted publishing published finishing_rollback finished }.each do |task| invoke "deploy:#{task}" end end end desc "Deploy a new release." task :deploy do set(:deploying, true) %w{ starting started updating updated publishing published finishing finished }.each do |task| invoke "deploy:#{task}" end end task default: :deploy capistrano-3.10.0/lib/capistrano/tasks/console.rake0000644000004100000410000000074613175754131022350 0ustar www-datawww-datadesc "Execute remote commands" task :console do stage = fetch(:stage) puts I18n.t("console.welcome", scope: :capistrano, stage: stage) loop do print "#{stage}> " command = (input = $stdin.gets) ? input.chomp : "exit" next if command.empty? if %w{quit exit q}.include? command puts t("console.bye") break else begin on roles :all do execute command end rescue => e puts e end end end end capistrano-3.10.0/lib/capistrano/tasks/deploy.rake0000644000004100000410000001657313175754131022207 0ustar www-datawww-datanamespace :deploy do task :starting do invoke "deploy:print_config_variables" if fetch(:print_config_variables, false) invoke "deploy:check" invoke "deploy:set_previous_revision" end task :print_config_variables do puts puts "------- Printing current config variables -------" env.keys.each do |config_variable_key| if is_question?(config_variable_key) puts "#{config_variable_key.inspect} => Question (awaits user input on next fetch(#{config_variable_key.inspect}))" else puts "#{config_variable_key.inspect} => #{fetch(config_variable_key).inspect}" end end puts puts "------- Printing current config variables of SSHKit mechanism -------" puts env.backend.config.inspect # puts env.backend.config.backend.config.ssh_options.inspect # puts env.backend.config.command_map.defaults.inspect puts end task updating: :new_release_path do invoke "deploy:set_current_revision" invoke "deploy:symlink:shared" end task :reverting do invoke "deploy:revert_release" end task :publishing do invoke "deploy:symlink:release" end task :finishing do invoke "deploy:cleanup" end task :finishing_rollback do invoke "deploy:cleanup_rollback" end task :finished do invoke "deploy:log_revision" end desc "Check required files and directories exist" task :check do invoke "deploy:check:directories" invoke "deploy:check:linked_dirs" invoke "deploy:check:make_linked_dirs" invoke "deploy:check:linked_files" end namespace :check do desc "Check shared and release directories exist" task :directories do on release_roles :all do execute :mkdir, "-p", shared_path, releases_path end end desc "Check directories to be linked exist in shared" task :linked_dirs do next unless any? :linked_dirs on release_roles :all do execute :mkdir, "-p", linked_dirs(shared_path) end end desc "Check directories of files to be linked exist in shared" task :make_linked_dirs do next unless any? :linked_files on release_roles :all do |_host| execute :mkdir, "-p", linked_file_dirs(shared_path) end end desc "Check files to be linked exist in shared" task :linked_files do next unless any? :linked_files on release_roles :all do |host| linked_files(shared_path).each do |file| unless test "[ -f #{file} ]" error t(:linked_file_does_not_exist, file: file, host: host) exit 1 end end end end end namespace :symlink do desc "Symlink release to current" task :release do on release_roles :all do tmp_current_path = release_path.parent.join(current_path.basename) execute :ln, "-s", release_path, tmp_current_path execute :mv, tmp_current_path, current_path.parent end end desc "Symlink files and directories from shared to release" task :shared do invoke "deploy:symlink:linked_files" invoke "deploy:symlink:linked_dirs" end desc "Symlink linked directories" task :linked_dirs do next unless any? :linked_dirs on release_roles :all do execute :mkdir, "-p", linked_dir_parents(release_path) fetch(:linked_dirs).each do |dir| target = release_path.join(dir) source = shared_path.join(dir) next if test "[ -L #{target} ]" execute :rm, "-rf", target if test "[ -d #{target} ]" execute :ln, "-s", source, target end end end desc "Symlink linked files" task :linked_files do next unless any? :linked_files on release_roles :all do execute :mkdir, "-p", linked_file_dirs(release_path) fetch(:linked_files).each do |file| target = release_path.join(file) source = shared_path.join(file) next if test "[ -L #{target} ]" execute :rm, target if test "[ -f #{target} ]" execute :ln, "-s", source, target end end end end desc "Clean up old releases" task :cleanup do on release_roles :all do |host| releases = capture(:ls, "-x", releases_path).split valid, invalid = releases.partition { |e| /^\d{14}$/ =~ e } warn t(:skip_cleanup, host: host.to_s) if invalid.any? if valid.count >= fetch(:keep_releases) info t(:keeping_releases, host: host.to_s, keep_releases: fetch(:keep_releases), releases: valid.count) directories = (valid - valid.last(fetch(:keep_releases))).map do |release| releases_path.join(release).to_s end if test("[ -d #{current_path} ]") current_release = capture(:readlink, current_path).to_s if directories.include?(current_release) warn t(:wont_delete_current_release, host: host.to_s) directories.delete(current_release) end else debug t(:no_current_release, host: host.to_s) end if directories.any? directories_str = directories.join(" ") execute :rm, "-rf", directories_str else info t(:no_old_releases, host: host.to_s, keep_releases: fetch(:keep_releases)) end end end end desc "Remove and archive rolled-back release." task :cleanup_rollback do on release_roles(:all) do last_release = capture(:ls, "-xt", releases_path).split.first last_release_path = releases_path.join(last_release) if test "[ `readlink #{current_path}` != #{last_release_path} ]" execute :tar, "-czf", deploy_path.join("rolled-back-release-#{last_release}.tar.gz"), last_release_path execute :rm, "-rf", last_release_path else debug "Last release is the current release, skip cleanup_rollback." end end end desc "Log details of the deploy" task :log_revision do on release_roles(:all) do within releases_path do execute :echo, %Q{"#{revision_log_message}" >> #{revision_log}} end end end desc "Revert to previous release timestamp" task revert_release: :rollback_release_path do on release_roles(:all) do set(:revision_log_message, rollback_log_message) end end task :new_release_path do set_release_path end task :rollback_release_path do on release_roles(:all) do releases = capture(:ls, "-xt", releases_path).split if releases.count < 2 error t(:cannot_rollback) exit 1 end rollback_release = ENV["ROLLBACK_RELEASE"] index = rollback_release.nil? ? 1 : releases.index(rollback_release) if index.nil? error t(:cannot_found_rollback_release, release: rollback_release) exit 1 end last_release = releases[index] set_release_path(last_release) set(:rollback_timestamp, last_release) end end desc "Place a REVISION file with the current revision SHA in the current release path" task :set_current_revision do on release_roles(:all) do within release_path do execute :echo, "\"#{fetch(:current_revision)}\" >> REVISION" end end end task :set_previous_revision do on release_roles(:all) do target = release_path.join("REVISION") if test "[ -f #{target} ]" set(:previous_revision, capture(:cat, target, "2>/dev/null")) end end end task :restart task :failed end capistrano-3.10.0/lib/capistrano/dotfile.rb0000644000004100000410000000012613175754131020660 0ustar www-datawww-datadotfile = Pathname.new(File.join(Dir.home, ".capfile")) load dotfile if dotfile.file? capistrano-3.10.0/lib/capistrano/proc_helpers.rb0000644000004100000410000000070113175754131021716 0ustar www-datawww-datamodule Capistrano module ProcHelpers module_function # Tests whether the given object appears to respond to `call` with # zero parameters. In Capistrano, such a proc is used to represent a # "deferred value". That is, a value that is resolved by invoking `call` at # the time it is first needed. def callable_without_parameters?(x) x.respond_to?(:call) && (!x.respond_to?(:arity) || x.arity.zero?) end end end capistrano-3.10.0/lib/capistrano/setup.rb0000644000004100000410000000166713175754131020405 0ustar www-datawww-datarequire "capistrano/doctor" require "capistrano/immutable_task" include Capistrano::DSL namespace :load do task :defaults do load "capistrano/defaults.rb" end end require "airbrussh/capistrano" # We don't need to show the "using Airbrussh" banner announcement since # Airbrussh is now the built-in formatter. Also enable command output by # default; hiding the output might be confusing to users new to Capistrano. Airbrussh.configure do |airbrussh| airbrussh.banner = false airbrussh.command_output = true end stages.each do |stage| Rake::Task.define_task(stage) do set(:stage, stage.to_sym) invoke "load:defaults" Rake.application["load:defaults"].extend(Capistrano::ImmutableTask) env.variables.untrusted! do load deploy_config_path load stage_config_path.join("#{stage}.rb") end configure_scm I18n.locale = fetch(:locale, :en) configure_backend end end require "capistrano/dotfile" capistrano-3.10.0/lib/capistrano/doctor/0000755000004100000410000000000013175754131020200 5ustar www-datawww-datacapistrano-3.10.0/lib/capistrano/doctor/servers_doctor.rb0000644000004100000410000000471313175754131023575 0ustar www-datawww-datarequire "capistrano/doctor/output_helpers" module Capistrano module Doctor class ServersDoctor include Capistrano::Doctor::OutputHelpers def initialize(env=Capistrano::Configuration.env) @servers = env.servers.to_a end def call title("Servers (#{servers.size})") rwc = RoleWhitespaceChecker.new(servers) table(servers) do |server, row| sd = ServerDecorator.new(server) row << sd.uri_form row << sd.roles row << sd.properties row.yellow if rwc.any_has_whitespace?(server.roles) end if rwc.whitespace_roles.any? warning "\nWhitespace detected in role(s) #{rwc.whitespace_roles_decorated}. " \ "This might be a result of a mistyped \"%w()\" array literal." end puts end private attr_reader :servers class RoleWhitespaceChecker attr_reader :whitespace_roles, :servers def initialize(servers) @servers = servers @whitespace_roles = find_whitespace_roles end def any_has_whitespace?(roles) roles.any? { |role| include_whitespace?(role) } end def include_whitespace?(role) role =~ /\s/ end def whitespace_roles_decorated whitespace_roles.map(&:inspect).join(", ") end private def find_whitespace_roles servers.map(&:roles).flat_map(&:to_a).uniq .select { |role| include_whitespace?(role) } end end class ServerDecorator def initialize(server) @server = server end def uri_form [ server.user, server.user && "@", server.hostname, server.port && ":", server.port ].compact.join end def roles server.roles.to_a.inspect end def properties return "" unless server.properties.keys.any? pretty_inspect(server.properties.to_h) end private attr_reader :server # Hashes with proper padding def pretty_inspect(element) return element.inspect unless element.is_a?(Hash) pairs_string = element.keys.map do |key| [pretty_inspect(key), pretty_inspect(element.fetch(key))].join(" => ") end.join(", ") "{ #{pairs_string} }" end end end end end capistrano-3.10.0/lib/capistrano/doctor/gems_doctor.rb0000644000004100000410000000225113175754131023032 0ustar www-datawww-datarequire "capistrano/doctor/output_helpers" module Capistrano module Doctor # Prints table of all Capistrano-related gems and their version numbers. If # there is a newer version of a gem available, call attention to it. class GemsDoctor include Capistrano::Doctor::OutputHelpers def call title("Gems") table(all_gem_names) do |gem, row| row.yellow if update_available?(gem) row << gem row << installed_gem_version(gem) row << "(update available)" if update_available?(gem) end end private def installed_gem_version(gem_name) Gem.loaded_specs[gem_name].version end def update_available?(gem_name) latest = Gem.latest_version_for(gem_name) return false if latest.nil? latest > installed_gem_version(gem_name) end def all_gem_names core_gem_names + plugin_gem_names end def core_gem_names %w(capistrano airbrussh rake sshkit net-ssh) & Gem.loaded_specs.keys end def plugin_gem_names (Gem.loaded_specs.keys - ["capistrano"]).grep(/capistrano/).sort end end end end capistrano-3.10.0/lib/capistrano/doctor/output_helpers.rb0000644000004100000410000000433213175754131023611 0ustar www-datawww-datamodule Capistrano module Doctor # Helper methods for pretty-printing doctor output to stdout. All output # (other than `title`) is indented by four spaces to facilitate copying and # pasting this output into e.g. GitHub or Stack Overflow to achieve code # formatting. module OutputHelpers class Row attr_reader :color attr_reader :values def initialize @values = [] end def <<(value) values << value end def yellow @color = :yellow end end # Prints a table for a given array of records. For each record, the block # is yielded two arguments: the record and a Row object. To print values # for that record, add values using `row << "some value"`. A row can # optionally be highlighted in yellow using `row.yellow`. def table(records, &block) return if records.empty? rows = collect_rows(records, &block) col_widths = calculate_column_widths(rows) rows.each do |row| line = row.values.each_with_index.map do |value, col| value.to_s.ljust(col_widths[col]) end.join(" ").rstrip line = color.colorize(line, row.color) if row.color puts line end end # Prints a title in blue with surrounding newlines. def title(text) # Use $stdout directly to bypass the indentation that our `puts` does. $stdout.puts(color.colorize("\n#{text}\n", :blue)) end # Prints text in yellow. def warning(text) puts color.colorize(text, :yellow) end # Override `Kernel#puts` to prepend four spaces to each line. def puts(string=nil) $stdout.puts(string.to_s.gsub(/^/, " ")) end private def collect_rows(records) records.map do |rec| Row.new.tap { |row| yield(rec, row) } end end def calculate_column_widths(rows) num_columns = rows.map { |row| row.values.length }.max Array.new(num_columns) do |col| rows.map { |row| row.values[col].to_s.length }.max end end def color @color ||= SSHKit::Color.new($stdout) end end end end capistrano-3.10.0/lib/capistrano/doctor/environment_doctor.rb0000644000004100000410000000074013175754131024444 0ustar www-datawww-datarequire "capistrano/doctor/output_helpers" module Capistrano module Doctor class EnvironmentDoctor include Capistrano::Doctor::OutputHelpers def call title("Environment") puts <<-OUT.gsub(/^\s+/, "") Ruby #{RUBY_DESCRIPTION} Rubygems #{Gem::VERSION} Bundler #{defined?(Bundler::VERSION) ? Bundler::VERSION : 'N/A'} Command #{$PROGRAM_NAME} #{ARGV.join(' ')} OUT end end end end capistrano-3.10.0/lib/capistrano/doctor/variables_doctor.rb0000644000004100000410000000354113175754131024052 0ustar www-datawww-datarequire "capistrano/doctor/output_helpers" module Capistrano module Doctor # Prints a table of all Capistrano variables and their current values. If # there are unrecognized variables, print warnings for them. class VariablesDoctor # These are keys that are recognized by Capistrano, but do not have values # set by default. WHITELIST = %i( application current_directory releases_directory repo_url repo_tree shared_directory ).freeze private_constant :WHITELIST include Capistrano::Doctor::OutputHelpers def initialize(env=Capistrano::Configuration.env) @env = env end def call title("Variables") values = inspect_all_values table(variables.keys.sort_by(&:to_s)) do |key, row| row.yellow if suspicious_keys.include?(key) row << key.inspect row << values[key] end puts if suspicious_keys.any? suspicious_keys.sort_by(&:to_s).each do |key| warning("#{key.inspect} is not a recognized Capistrano setting "\ "(#{location(key)})") end end private attr_reader :env def variables env.variables end def inspect_all_values variables.keys.each_with_object({}) do |key, inspected| inspected[key] = if env.is_question?(key) "" else variables.peek(key).inspect end end end def suspicious_keys (variables.untrusted_keys & variables.unused_keys) - WHITELIST end def location(key) loc = variables.source_locations(key).first loc && loc.sub(/^#{Regexp.quote(Dir.pwd)}/, "").sub(/:in.*/, "") end end end end capistrano-3.10.0/lib/capistrano/deploy.rb0000644000004100000410000000013013175754131020521 0ustar www-datawww-datarequire "capistrano/framework" load File.expand_path("../tasks/deploy.rake", __FILE__) capistrano-3.10.0/lib/capistrano/dsl.rb0000644000004100000410000000622013175754131020015 0ustar www-datawww-datarequire "capistrano/dsl/task_enhancements" require "capistrano/dsl/paths" require "capistrano/dsl/stages" require "capistrano/dsl/env" require "capistrano/configuration/filter" module Capistrano module DSL include TaskEnhancements include Env include Paths include Stages def invoke(task_name, *args) task = Rake::Task[task_name] # NOTE: We access instance variable since the accessor was only added recently. Once Capistrano depends on rake 11+, we can revert the following line if task && task.instance_variable_get(:@already_invoked) file, line, = caller.first.split(":") colors = SSHKit::Color.new($stderr) $stderr.puts colors.colorize("Skipping task `#{task_name}'.", :yellow) $stderr.puts "Capistrano tasks may only be invoked once. Since task `#{task}' was previously invoked, invoke(\"#{task_name}\") at #{file}:#{line} will be skipped." $stderr.puts "If you really meant to run this task again, use invoke!(\"#{task_name}\")" $stderr.puts colors.colorize("THIS BEHAVIOR MAY CHANGE IN A FUTURE VERSION OF CAPISTRANO. Please join the conversation here if this affects you.", :red) $stderr.puts colors.colorize("https://github.com/capistrano/capistrano/issues/1686", :red) end task.invoke(*args) end def invoke!(task_name, *args) task = Rake::Task[task_name] task.reenable task.invoke(*args) end def t(key, options={}) I18n.t(key, options.merge(scope: :capistrano)) end def scm fetch(:scm) end def sudo(*args) execute :sudo, *args end def revision_log_message fetch(:revision_log_message, t(:revision_log_message, branch: fetch(:branch), user: local_user, sha: fetch(:current_revision), release: fetch(:release_timestamp))) end def rollback_log_message t(:rollback_log_message, user: local_user, release: fetch(:rollback_timestamp)) end def local_user fetch(:local_user) end def lock(locked_version) VersionValidator.new(locked_version).verify end # rubocop:disable Security/MarshalLoad def on(hosts, options={}, &block) subset_copy = Marshal.dump(Configuration.env.filter(hosts)) SSHKit::Coordinator.new(Marshal.load(subset_copy)).each(options, &block) end # rubocop:enable Security/MarshalLoad def run_locally(&block) SSHKit::Backend::Local.new(&block).run end # Catch common beginner mistake and give a helpful error message on stderr def execute(*) file, line, = caller.first.split(":") colors = SSHKit::Color.new($stderr) $stderr.puts colors.colorize("Warning: `execute' should be wrapped in an `on' scope in #{file}:#{line}.", :red) $stderr.puts $stderr.puts " task :example do" $stderr.puts colors.colorize(" on roles(:app) do", :yellow) $stderr.puts " execute 'whoami'" $stderr.puts colors.colorize(" end", :yellow) $stderr.puts " end" $stderr.puts raise NoMethodError, "undefined method `execute' for main:Object" end end end extend Capistrano::DSL capistrano-3.10.0/lib/capistrano/framework.rb0000644000004100000410000000013013175754131021222 0ustar www-datawww-dataload File.expand_path("../tasks/framework.rake", __FILE__) require "capistrano/install" capistrano-3.10.0/lib/capistrano.rb0000644000004100000410000000000013175754131017221 0ustar www-datawww-datacapistrano-3.10.0/.rubocop.yml0000644000004100000410000000227313175754131016253 0ustar www-datawww-dataAllCops: DisplayCopNames: true DisplayStyleGuide: true TargetRubyVersion: 2.0 Lint/AmbiguousBlockAssociation: Enabled: false Metrics/BlockLength: Exclude: - "spec/**/*" - "lib/**/*.rake" Style/BarePercentLiterals: EnforcedStyle: percent_q Style/ClassAndModuleChildren: Enabled: false Style/DoubleNegation: Enabled: false Style/FileName: Exclude: - "Dangerfile" Style/IndentHeredoc: Enabled: false Style/SpaceAroundEqualsInParameterDefault: EnforcedStyle: no_space Style/StringLiterals: EnforcedStyle: double_quotes Style/TrivialAccessors: AllowPredicates: true Style/PercentLiteralDelimiters: Enabled: false Style/SingleLineBlockParams: Enabled: false Style/ModuleFunction: Enabled: false # Enable someday Style/Documentation: Enabled: false # Needs refactors Metrics/PerceivedComplexity: Enabled: false Metrics/CyclomaticComplexity: Enabled: false Metrics/MethodLength: Enabled: false Style/PredicateName: Enabled: false Metrics/LineLength: Enabled: false Metrics/AbcSize: Enabled: false Style/PerlBackrefs: Enabled: false Metrics/ClassLength: Enabled: false Metrics/ModuleLength: Enabled: false Style/AccessorMethodName: Enabled: false capistrano-3.10.0/.gitignore0000644000004100000410000000034213175754131015764 0ustar www-datawww-data*.gem *.rbc *.swp .bundle .config .rspec .rspec-local .ruby-version .yardoc Gemfile.lock InstalledFiles _site _yardoc coverage doc/ lib/bundler/man pkg rdoc spec/reports tags test/tmp test/version_tmp tmp /docs/_site/ vendor/ capistrano-3.10.0/CONTRIBUTING.md0000644000004100000410000000671113175754131016233 0ustar www-datawww-data**Hello and welcome!** Please look over this document before opening an issue or submitting a pull request to Capistrano. * [If you’re looking for help or have a question](#if-youre-looking-for-help-or-have-a-question) * [Reporting bugs](#reporting-bugs) * [Requesting new features or improvements](#requesting-new-features-or-improvements) * [Contributing code or documentation](#contributing-code-or-documentation) ## If you’re looking for help or have a question **Check [Stack Overflow](http://stackoverflow.com/questions/tagged/capistrano) first if you need help using Capistrano.** You are more likely to get a quick response at Stack Overflow for common Capistrano topics. Make sure to tag your post with `capistrano` and/or `capistrano3` (not forgetting any other tags which might relate: rvm, rbenv, Ubuntu, etc.) If you have an urgent problem you can also try [CodersClan](http://codersclan.net/?repo_id=325&source=contributing), which has a community of Capistrano experts dedicated to solve code problems for bounties. When posting to Stack Overflow or CodersClan, be sure to include relevant information: * Capistrano version * Plugins and versions (capistrano-rvm, capistrano-bundler, etc.) * Logs and backtraces If you think you’ve found a bug in Capistrano itself, then… ## Reporting bugs As much the Capistrano community tries to write good, well-tested code, bugs still happen. Sorry about that! **In case you’ve run across an already-known issue, check the FAQs first on the [official Capistrano site](http://capistranorb.com).** When opening a bug report, please include the output of the `cap doctor` task, e.g.: ``` cap production doctor ``` Also include in your report: * Versions of Ruby, Capistrano, and any plugins you’re using (if `doctor` didn't already do this for you) * A description of the troubleshooting steps you’ve taken * Logs and backtraces * Sections of your `deploy.rb` that may be relevant * Any other unique aspects of your environment 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!* If you think you may have discovered a security vulnerability in Capistrano, do not open a GitHub issue. Instead, please send a report to . ## Requesting new features or improvements Capistrano continues to improve thanks to people like you! Feel free to open a GitHub issue for any or all of these ideas: * New features that would make Capistrano even better * Areas where documentation could be improved * Ways to improve developer happiness Generally speaking the maintainers are very conservative about adding new features, and we can’t guarantee that the community will agree with or implement your idea. Please don’t be offended if we say no! The Capistrano team will do our best to review all suggestions and at least weigh in with a comment or suggest a workaround, if applicable. **Your idea will have a much better chance of becoming reality if you contribute code for it (even if the code is incomplete!).** ## Contributing code or documentation So you want to contribute to Capistrano? Awesome! We have a whole separate document just you. It explains our pull request workflow and walks you through setting up the development environment: [DEVELOPMENT.md][]. [DEVELOPMENT.md]: https://github.com/capistrano/capistrano/blob/master/DEVELOPMENT.md capistrano-3.10.0/.github/0000755000004100000410000000000013175754131015335 5ustar www-datawww-datacapistrano-3.10.0/.github/issue_template.md0000644000004100000410000000203413175754131020701 0ustar www-datawww-data**Important:** GitHub issues are for feature requests or bug reports. The Capistrano team recommends you use [Stack Overflow](http://stackoverflow.com/questions/tagged/capistrano) for general questions. For more details, please see our [contribution policy](https://github.com/capistrano/capistrano/blob/master/CONTRIBUTING.md). --- ### Steps to reproduce If reasonable, you can help by creating a Capistrano skeleton example project which reproduces the issue you are seeing. You can then upload the individual files to a GitHub Gist or a GitHub project. Others can simply modify the configuration to point at a test server/repository of their own. Often times, an issue is resolved simply by making this test case. An example test case is here: https://gist.github.com/will-in-wi/527327e31af30b3eae2068e2965be05b ### Expected behavior Tell us what should happen ### Actual behavior Tell us what happens instead ### System configuration Please link to the output of `cap doctor` in a GitHub Gist. Thanks for helping improve Capistrano! capistrano-3.10.0/.github/pull_request_template.md0000644000004100000410000000175313175754131022304 0ustar www-datawww-data### Summary (Guidelines for creating a bug report are available here: https://github.com/capistrano/capistrano/blob/master/DEVELOPMENT.md) Provide a general description of the code changes in your pull request... were there any bugs you had fixed? If so, mention them. If these bugs have open GitHub issues, be sure to tag them here as well, to keep the conversation linked together. ### Short checklist - [ ] Did you run `bundle exec rubocop -a` to fix linter issues? - [ ] If relevant, did you create a test? - [ ] Did you confirm that the RSpec tests pass? - [ ] If you are fixing a bug or introducing a new feature, did you add a CHANGELOG entry? ### Other Information If there's anything else that's important and relevant to your pull request, mention that information here. If you are updating any of the CHANGELOG files or are asked to update the CHANGELOG files by reviewers, please add the CHANGELOG entry at the top of the file where indicated. Thanks for helping improve Capistrano! capistrano-3.10.0/RELEASING.md0000644000004100000410000000146213175754131015633 0ustar www-datawww-data# Releasing ## Prerequisites * You must have commit rights to the Capistrano repository. * You must have push rights for the capistrano gem on rubygems.org. ## How to release 1. Run `bundle install` to make sure that you have all the gems necessary for testing and releasing. 2. **Ensure all tests are passing by running `rake spec` and `rake features`.** 3. Determine which would be the correct next version number according to [semver](http://semver.org/). 4. Update the version in `./lib/capistrano/version.rb`. 4. Update the version in the `./README.md` Gemfile example (`gem "capistrano", "~> X.Y"`). 5. Update the `CHANGELOG`. 6. Commit the changelog and version in a single commit, the message should be "Preparing vX.Y.Z" 7. Run `rake release`; this will tag, push to GitHub, and publish to rubygems.org. capistrano-3.10.0/CHANGELOG.md0000644000004100000410000007362113175754131015617 0ustar www-datawww-data# Capistrano 3.x Changelog All notable changes to this project will be documented in this file, in reverse chronological order. **Capistrano follows a modified version of [SemVer](http://semver.org)**, similar to the Ruby on Rails project. For a `X.Y.Z` release: * `Z` indicates bug fixes only; no breaking changes and no new features, except as necessary for security fixes. * `Y` is bumped when we add new features. Occasionally a `Y` release may include small breaking changes. We will notify via CHANGELOG entries and/or deprecation notices if there are breaking changes. * `X` is incremented for significant breaking changes. This is reserved for special occasions, like a complete rewrite. **Capistrano uses a six-week release cadence.** Every six weeks, give or take, any changes in master will be published as a new rubygems version. If you'd like to use a feature or fix that is in master and you can't wait for the next planned release, put this in your project's Gemfile to use the master branch directly: ```ruby gem "capistrano", :github => "capistrano/capistrano" ``` ## [master] [master]: https://github.com/capistrano/capistrano/compare/v3.10.0...HEAD * Your contribution here! ## [`3.10.0`] (2017-10-23) [`3.10.0`]: https://github.com/capistrano/capistrano/compare/v3.9.1...v3.10.0 As of this release, version 2.x of Capistrano is officially End of Life. No further releases of 2.x series are planned, and pull requests against 2.x are no longer accepted. The maintainers encourage you to upgrade to 3.x if possible. ### Breaking changes: * None ### New features: * [#1943](https://github.com/capistrano/capistrano/issues/1943): Make 'releases' and 'shared' directory names configurable from deployment target * [#1922](https://github.com/capistrano/capistrano/pull/1922): Prevents last good release from being deleted during cleanup if there are too many subsequent failed deploys * [#1930](https://github.com/capistrano/capistrano/issues/1930): Default to locking the version using the pessimistic version operator at the patch level. ### Fixes: * [#1937](https://github.com/capistrano/capistrano/pull/1937): Clarify error message when plugin is required in the wrong config file. ## [`3.9.1`] (2017-09-08) [`3.9.1`]: https://github.com/capistrano/capistrano/compare/v3.9.0...v3.9.1 ### Breaking changes: * None ### Fixes: * [#1912](https://github.com/capistrano/capistrano/pull/1912): Fixed an issue where questions posed by `ask` were not printed on certain platforms - [@kminiatures](https://github.com/kminiatures) ## [`3.9.0`] (2017-07-28) [`3.9.0`]: https://github.com/capistrano/capistrano/compare/v3.8.2...v3.9.0 ### Breaking changes: * None ### New features: * [#1911](https://github.com/capistrano/capistrano/pull/1911): Add Capistrano::DSL#invoke! for repetitive tasks ### Fixes: * [#1899](https://github.com/capistrano/capistrano/pull/1899): Updated `deploy:cleanup` to continue rotating the releases and skip the invalid directory names instead of skipping the whole rotation of releases. The warning message has changed slightly due to the change of behavior. ## [`3.8.2`] (2017-06-16) [`3.8.2`]: https://github.com/capistrano/capistrano/compare/v3.8.1...v3.8.2 ### Breaking changes: * None ### Other changes: * [#1882](https://github.com/capistrano/capistrano/pull/1882): Explain where to add new Capfile lines in scm deprecation warning - [@robd](https://github.com/robd) ## [`3.8.1`] (2017-04-21) [`3.8.1`]: https://github.com/capistrano/capistrano/compare/v3.8.0...v3.8.1 ### Breaking changes: * None ### Fixes: * [#1867](https://github.com/capistrano/capistrano/pull/1867): Allow `cap -T` to run without Capfile present - [@mattbrictson](https://github.com/mattbrictson) ## [`3.8.0`] (2017-03-10) [`3.8.0`]: https://github.com/capistrano/capistrano/compare/v3.7.2...v3.8.0 ### Minor breaking changes: * [#1846](https://github.com/capistrano/capistrano/pull/1846): add_host - When this method has already been called once for a given host and it is called a second time with a port, a new host will be added. Previously, the first host would have been updated. [(@dbenamy)](https://github.com/dbenamy) ### New features: * [#1860](https://github.com/capistrano/capistrano/pull/1860): Allow cap to be run within subdir and still work - [@mattbrictson](https://github.com/mattbrictson) ### Fixes: * [#1835](https://github.com/capistrano/capistrano/pull/1835): Stopped printing parenthesis in ask prompt if no default or nil was passed as argument [(@chamini2)](https://github.com/chamini2) * [#1840](https://github.com/capistrano/capistrano/pull/1840): Git plugin: shellescape git_wrapper_path [(@olleolleolle)](https://github.com/olleolleolle) * [#1843](https://github.com/capistrano/capistrano/pull/1843): Properly shell escape git:wrapper steps - [@mattbrictson](https://github.com/mattbrictson) * [#1846](https://github.com/capistrano/capistrano/pull/1846): Defining a role is now O(hosts) instead of O(hosts^2) [(@dbenamy)](https://github.com/dbenamy) * Run `svn switch` to work with svn branches if repo_url is changed * [#1856](https://github.com/capistrano/capistrano/pull/1856): Fix hg repo_tree implementation - [@mattbrictson](https://github.com/mattbrictson) * [#1857](https://github.com/capistrano/capistrano/pull/1857): Don't emit doctor warning when repo_tree is set - [@mattbrictson](https://github.com/mattbrictson) ### Other changes: * [capistrano-harrow#4](https://github.com/harrowio/capistrano-harrow/issues/4): Drop dependency on `capistrano-harrow` gem. Gem can still be installed separately [(@leehambley)](https://github.com/leehambley) * [#1859](https://github.com/capistrano/capistrano/pull/1859): Move git-specific repo_url logic into git plugin - [@mattbrictson](https://github.com/mattbrictson) * [#1858](https://github.com/capistrano/capistrano/pull/1858): Unset the :scm variable when an SCM plugin is used - [@mattbrictson](https://github.com/mattbrictson) ## [`3.7.2`] (2017-01-27) [`3.7.2`]: https://github.com/capistrano/capistrano/compare/v3.7.1...v3.7.2 ### Potentially breaking changes: * None ### Other changes: * Suppress log messages of `git ls-remote` by filtering remote refs (@aeroastro) * The Git SCM now allows the repo_url to be changed without manually wiping out the mirror on each target host first (@javanthropus) ## [`3.7.1`] (2016-12-16) [`3.7.1`]: https://github.com/capistrano/capistrano/compare/v3.7.0...v3.7.1 ### Potentially breaking changes: * None ### Fixes: * Fixed a bug with mercurial deploys failing due to an undefined variable ## [`3.7.0`] (2016-12-10) [`3.7.0`]: https://github.com/capistrano/capistrano/compare/v3.6.1...v3.7.0 *Note: These release notes include all changes since 3.6.1, including the changes that were first published in 3.7.0.beta1.* ### Deprecations: * The `set :scm, ...` mechanism is now deprecated in favor of a new SCM plugin system. See the [UPGRADING-3.7](UPGRADING-3.7.md) document for details ### Potentially breaking changes: * The `:git_strategy`, `:hg_strategy`, and `:svn_strategy` settings have been removed with no replacement. If you have been using these to customize Capistrano's SCM behavior, you will need to rewrite your customization using the [new plugin system](http://capistranorb.com/documentation/advanced-features/custom-scm/) * `remote_file` feature has been removed and is no longer available to use @SaiVardhan ### New features: * The `tar` used by the Git SCM now honors the SSHKit command map, allowing an alternative tar binary to be used (e.g. gtar) #1787 (@caius) * Add support for custom on-filters [#1776](https://github.com/capistrano/capistrano/issues/1776) ### Fixes: * Fix test suite to work with Mocha 1.2.0 (@caius) * Fix bug where host_filter and role_filter were overly greedy [#1766](https://github.com/capistrano/capistrano/issues/1766) (@cseeger-epages) * Fix the removal of old releases `deploy:cleanup`. Logic is changed because of unreliable modification times on folders. Removal of directories is now decided by sorting on folder names (name is generated from current datetime format YmdHis). Cleanup is skipped, and a warning is given when a folder name is in a different format ## [`3.7.0.beta1`] (2016-11-02) [`3.7.0.beta1`]: https://github.com/capistrano/capistrano/compare/v3.6.1...v3.7.0.beta1 ### Deprecations: * The `set :scm, ...` mechanism is now deprecated in favor of a new SCM plugin system. See the [UPGRADING-3.7](UPGRADING-3.7.md) document for details. ### Potentially breaking changes: * The `:git_strategy`, `:hg_strategy`, and `:svn_strategy` settings have been removed with no replacement. If you have been using these to customize Capistrano's SCM behavior, you will need to rewrite your customization using the [new plugin system](http://capistranorb.com/documentation/advanced-features/custom-scm/). * `remote_file` feature has been removed and is no longer available to use @SaiVardhan ### New features: * The `tar` used by the Git SCM now honors the SSHKit command map, allowing an alternative tar binary to be used (e.g. gtar) #1787 (@caius) ### Fixes: * Fix test suite to work with Mocha 1.2.0 (@caius) * Fix bug where host_filter and role_filter were overly greedy [#1766](https://github.com/capistrano/capistrano/issues/1766) (@cseeger-epages) ## [`3.6.1`] (2016-08-23) [`3.6.1`]: https://github.com/capistrano/capistrano/compare/v3.6.0...v3.6.1 ### Fixes: * Restore compatibility with older versions of Rake (< 11.0.0) (@troelskn) * Fix `NoMethodError: undefined method gsub` when setting `:application` to a Proc. The original fix released in 3.6.0 worked for values specified with blocks, but not for those specified with procs or lambdas (the latter syntax is much more common). [#1681](https://github.com/capistrano/capistrano/issues/1681) * Fix a bug where deploy would fail if `:local_user` contained a space; spaces are now replaced with dashes when computing the git-ssh suffix. (@will_in_wi) ## [`3.6.0`] (2016-07-26) [`3.6.0`]: https://github.com/capistrano/capistrano/compare/v3.5.0...v3.6.0 Thank you to the many first-time contributors from the Capistrano community who helped with this release! ### Deprecations: * Deprecate `remote_file` feature (will be removed in Capistrano 3.7.0) (@lebedev-yury) * Deprecate `:git_strategy`, `:hg_strategy`, and `:svn_strategy` variables. These will be completely removed in 3.7.0. * Added warning about future deprecation of reinvocation behaviour (@troelskn) Refer to the [Capistrano 3.7.0 upgrade document](UPGRADING-3.7.md) if you are affected by these deprecations. ### New features: * Added a `doctor:servers` subtask that outputs a summary of servers, roles & properties (@irvingwashington) * Make path to git wrapper script configurable (@thickpaddy) * Make name of current directory configurable via configuration variable `:current_directory` (@websi) * It is now possible to rollback to a specific release using the `ROLLBACK_RELEASE` environment variable. [#1155](https://github.com/capistrano/capistrano/issues/1155) (@lanrion) ### Fixes: * `doctor` no longer erroneously warns that `:git_strategy` and other SCM options are "unrecognized" (@shanesaww) * Fix `NoMethodError: undefined method gsub` when setting `:application` to a Proc. [#1681](https://github.com/capistrano/capistrano/issues/1681) (@mattbrictson) ### Other changes: * Raise a better error when an ‘after’ hook isn’t found (@jdelStrother) * Change git wrapper path to work better with multiple users (@thickpaddy) * Restrict the uploaded git wrapper script permissions to 700 (@irvingwashington) * Add `net-ssh` gem version to `doctor:gems` output (@lebedev-yury) ## [`3.5.0`] [`3.5.0`]: https://github.com/capistrano/capistrano/compare/v3.4.1...v3.5.0 **You'll notice a big cosmetic change in this release: the default logging format has been changed to [Airbrussh](https://github.com/mattbrictson/airbrussh).** For more details on what Airbrussh does and how to configure it, visit the [Airbrussh README](https://github.com/mattbrictson/airbrussh#readme). * To opt out of the new format, simply add `set :format, :pretty` to switch to the old default of Capistrano 3.4.0 and earlier. * If you are already an Airbrussh user, note that the default configuration has changed, and the syntax for configuring Airbrussh has changed as well. [This simple upgrade guide](https://github.com/mattbrictson/airbrussh/blob/master/UPGRADING-CAP-3.5.md) will walk you through it. ### Potentially breaking changes: * Drop support for Ruby 1.9.3 (Capistrano does no longer work with 1.9.3) * Git version 1.6.3 or greater is now required * Remove 'vendor/bundle' from default :linked_dirs (@ojab) * Old versions of SSHKit (before 1.9.0) are no longer supported * SHA1 hash of current git revision written to REVISION file is no longer abbreviated * Ensure task invocation within after hooks is namespace aware, which may require you to change how your `after` hooks are declared in some cases; see [#1652](https://github.com/capistrano/capistrano/issues/1652) for an example and how to correct it (@thickpaddy) * Validation of the `:application` variable forbids special characters such as slash, this may be a breaking change in case that you rely on using a `/` in your application name to deploy from a sub directory. ### New features: * Added a `doctor` task that outputs helpful troubleshooting information. Try it like this: `cap production doctor`. (@mattbrictson) * Added a `dry_run?` helper method * `remove` DSL method for removing values like from arrays like `linked_dirs` * `append` DSL method for pushing values like `linked_dirs` [#1447](https://github.com/capistrano/capistrano/pull/1447), [#1586](https://github.com/capistrano/capistrano/pull/1586) * Added support for git shallow clone * Added new runtime option `--print-config-variables` that inspect all defined config variables in order to assist development of new capistrano tasks (@gerardo-navarro) * Prune dead tracking branches from git repositories while updating * Added options to set username and password when using Subversion as SCM (@dsthode) * Allow after() to refer to tasks that have not been loaded yet (@jcoglan) * Allow use "all" as string for server filtering (@theist) * Print a warning and abort if "load:defaults" is erroneously invoked after capistrano is already loaded, e.g. when a plugin is loaded in `deploy.rb` instead of `Capfile`. (@mattbrictson) * Added option to set specific revision when using Subversion as SCM (@marcovtwout) * Deduplicate list of linked directories * Integration with Harrow.io (See http://capistranorb.com/documentation/harrow/) when running `cap install` * Added validate method to DSL to allow validation of certain values (@Kriechi) * validate values before assignment inside of `set(:key, value)` * should raise a `Capistrano::ValidationError` if invalid * Added default validation for Capistrano-specific variables (@Kriechi) ### Fixes: * Capistrano is now fully-compatible with Rake 11.0. (@mattbrictson) * Fix filtering behaviour when using literal hostnames in on() block (@townsen) * Allow dot in :application name (@marcovtwout) * Fixed git-ssh permission error (@spight) ### Other changes: * Internal Rubocop cleanups. * Removed the post-install message (@Kriechi) * Refactor `Configuration::Filter` to use filtering strategies instead of case statements (@cshaffer) * Clean up rubocop lint warnings (@cshaffer) ## [`3.4.0`] [`3.4.0`]: https://github.com/capistrano/capistrano/compare/v3.3.5...v3.4.0 * Fixed fetch revision for annotated git tags. (@igorsokolov) * Fixed updating roles when custom user or port is specified. (@ayastreb) * Disables statistics collection. * `bin/` is not suggested to be in `linked_dirs` anymore (@kirs) * bin/ is often checked out into repo * https://github.com/capistrano/bundler/issues/45#issuecomment-69349237 * Bugfix: * release_roles did not honour additional property filtering (@townsen) * Refactored and simplified property filtering code (@townsen) * Breaking Changes * Hosts with the same name are now consolidated into one irrespective of the user and port. This allows multiple declarations of a server to be made safely. The last declared properties will win. See capistranorb.com Properties documentation for details. * Inside the on() block the host variable is now a copy of the host, so changes can be made within the block (such as dynamically overriding the user) that will not persist. This is very convenient for switching the SSH user temporarily to 'root' for example. * Minor changes * Add role_properties() method (see capistrano.github.io PR for doc) (@townsen) * Add equality syntax ( eg. port: 1234) for property filtering (@townsen) * Add documentation regarding property filtering (@townsen) * Clarify wording and recommendation in stage template. (@Kriechi) * Both available syntaxes provide similar functionality, do not use both for the same server+role combination. * Allow specification of repo_path using stage variable default is as before (@townsen) ## [`3.3.5`] [`3.3.5`]: https://github.com/capistrano/capistrano/compare/v3.3.4...v3.3.5 * Fixed setting properties twice when creating new server. See [issue #1214](https://github.com/capistrano/capistrano/issues/1214) (@ayastreb) ## [`3.3.4`] [`3.3.4`]: https://github.com/capistrano/capistrano/compare/v3.3.3...v3.3.4 * Minor changes: * Rely on a newer version of capistrano-stats with better privacy (@leehambley) * Fix cucumber spec for loading tasks from stage configs (@sponomarev) * Minor documentation fixes (@deeeki, @seuros, @andresilveira) * Spec improvements (@dimitrid, @sponomarev) * Fix to CLI flags for git-ls-remote (@dimitrid) ## [`3.3.3`] [`3.3.3`]: https://github.com/capistrano/capistrano/compare/v3.2.1...v3.3.3 * Enhancement (@townsen) * Added the variable `:repo_tree` which allows the specification of a sub-tree that will be extracted from the repository. This is useful when deploying a project that lives in a subdirectory of a larger repository. Implemented only for git and hg. If not defined then the behaviour is as previously and the whole repository is extracted (subject to git-archive `.gitattributes` of course). * Enhancement (@townsen): Remove unnecessary entries from default backtrace When the `--backtrace` (or `--trace`) command line option is not supplied Rake lowers the noise level in exception backtraces by building a regular expression containing all the system library paths and using it to exclude backtrace entries that match. This does not always go far enough, particularly in RVM environments when many gem paths are added. This commit reverses that approach and _only_ include backtrace entries that fall within the Capfile and list of tasks imported thereafter. This makes reading exceptions much easier on the eye. If the full unexpurgated backtrace is required then the --backtrace and --trace options supply it as before. * Disable loading stages configs on `cap -T` (@sponomarev) * Enhancements (@townsen) * Fix matching on hosts with custom ports or users set * Previously filtering would affect any generated configuration files so that files newly deployed would not be the same as those on the hosts previously deployed (and now excluded by filters). This is almost certainly not what is wanted: the filters should apply only to the on() method and thus any configuration files deployed will be identical across the set of servers making up the stage. * Host and Role filtering now affects only `on()` commands and not the `roles()`, `release_roles()` and `primary()` methods. * This applies to filters defined via the command line, the environment and the :filter variable. * Filtering now supports Regular expressions * This change _could_ cause existing scripts that use filtering and depend on the old behaviour to fail, though it is unlikely. Users who rely on filtering should check that generated configuration files are correct, and where not introduce server properties to do the filtering. For example, if a filter was used to specify an active subset of servers (by hostname), it should be removed and replaced with an 'active' property (set to true or false) on the server definitions. This keeps the stage file as the canonical model of the deployment environment. * See the documentation in the README.md file * Enhancements (@townsen) * Added set_if_empty method to DSL to allow conditional setting * Altered standard Capistrano defaults so that they are not set at the start of a stage if they have been previously set. This allows variables like :default_env to be set in deploy.rb. * Deep copy properties added using the 'roles' keyword * If a property exists on a server when another definition is encountered and is an Array, Set or Hash then add the new values This allows roles to specify properties common to all servers and then for individual servers to modify them, keeping things DRY Breaking Changes: * By using Ruby's noecho method introduced in Ruby version 1.9.3, we dropped support for Ruby versions prior to 1.9.3. See [issue #878](https://github.com/capistrano/capistrano/issues/878) and [PR #1112](https://github.com/capistrano/capistrano/pull/1112) for more information. (@kaikuchn) * Track (anonymous) statistics, see https://github.com/capistrano/stats. This breaks automated deployment on continuous integration servers until the `.capistrano/metrics` file is created (with content `full` to simulate a "yes") via the interactive prompt or manually. * Bug Fixes: * Fixed compatibility with FreeBSD tar (@robbertkl) * remote_file can be used inside a namespace (@mikz) * Minor Changes * Remove -v flag from mkdir call. (@caligo-mentis) * Capistrano now allows to customize `local_user` for revision log. (@sauliusgrigaitis) * Added tests for after/before hooks features (@juanibiapina, @miry) * Added `--force` flag to `svn export` command to fix errors when the release directory already exists. * Improved the output of `cap --help`. (@mbrictson) * Cucumber suite now runs on the latest version of Vagrant (@tpett) * The `ask` method now supports the `echo: false` option. (@mbrictson, @kaikuchn) * Cucumber scenario improvements (@bruno-) * Added suggestion to Capfile to use 'capistrano-passenger' gem, replacing suggestion in config/deploy.rb to re-implement 'deploy:restart' (@betesh) * Updated svn fetch_revision method to use `svnversion` * `cap install` no longer overwrites existing files. (@dmarkow) ## [`3.2.1`] [`3.2.1`]: https://github.com/capistrano/capistrano/compare/v3.2.0...v3.2.1 * Bug Fixes: * 3.2.0 introduced some behaviour to modify the way before/after hooks were called, to allow the optional preservation of arguments to be passed to tasks. This release reverts that commit in order to restore original functionality, and fix (fairly serious) bugs introduced by the refactoring. * Minor changes: * Update dsl#local_user method and add test for it. (@bruno-) * Revert short sha1 revision with git. (@blaugueux) * Changed asking question to more standard format (like common unix commandline tools) (@sponomarev) * Fixed typos in the README. (@sponomarev) * Added `keys` method to Configuration to allow introspection of configuration options. (@juanibiapina) * Improve error message when git:check fails (raise instead of silently `exit 1`) (@mbrictson) ## [`3.2.0`] The changelog entries here are incomplete, because many authors choose not to be credited for their work, check the tag comparison link for Github. [`3.2.0`]: https://github.com/capistrano/capistrano/compare/v3.1.0...v3.2.0 * Minor changes: * Added `keys` method to Server properties to allow introspection of automatically added properties. * Compatibility with Rake 10.2.0 - `ensure_task` is now added to `@top_level_tasks` as a string. (@dmarkow) * Amended the git check command, "ls-remote", to use "-h", limiting the list to refs/heads ## [`3.1.0`] [`3.1.0`]: https://github.com/capistrano/capistrano/compare/v3.0.1...v3.1.0 Breaking changes: * `deploy:restart` task **is no longer run by default**. From this version, developers who restart the app on each deploy need to declare it in their deploy flow (eg `after 'deploy:publishing', 'deploy:restart'`) or, for passenger applications, use the capistrano-passenger gem. Please, check https://github.com/capistrano/capistrano/commit/4e6523e1f50707499cf75eb53dce37a89528a9b0 for more information. (@kirs) * Minor changes * Tasks that used `linked_dirs` and `linked_files` now run on all roles, not just app roles (@mikespokefire) * Tasks `deploy:linked_dirs`, `deploy:make_linked_dirs`, `deploy:linked_files`, `deploy:cleanup_rollback`, `deploy:log_revision` and `deploy:revert_release` now use `release_roles()` not `roles()` meaning that they will only run on servers where the `no_release` property is not falsy. (@leehambley) * Fixed bug when `deploy:cleanup` was executed twice by default (@kirs) * Config location can now be changed with `deploy_config_path` and `stage_config_path` options (@seenmyfate) * `no_release` option is now available (@seenmyfate) * Raise an error if developer tries to define `:all` role, which is reserved (@kirs) * `deploy:failed` hook was added to add some custom behaviour on failed deploy (@seenmyfate) * Correctly infer namespace in task enhancements (@seenmyfate) * Add SHA to revision log (@blackxored) * Allow configuration of multiple servers with same hostname but different ports (@rsslldnphy) * Add command line option to control role filtering (@andytinycat) * Make use of recent changes in Rake to over-ride the application name (@shime) * Readme corrections (@nathanstitt) * Allow roles to be fetched with a variable containing an array (@seenmyfate) * Improve console (@jage) * Add ability to filter tasks to specific servers (host filtering). (@andytinycat) * Add a command line option to control role filter (`--roles`) (@andytinycat) * Use an SCM object with a pluggable strategy (@coffeeaddict) Big thanks to @Kriechi for his help. ## [`3.0.1`] [`3.0.1`]: https://github.com/capistrano/capistrano/compare/v3.0.0...v3.0.1 * `capify` not listed as executable (@leehambley) * Confirm license as MIT (@leehambley) * Move the git ssh helper to application path (@mpapis) ## [`3.0.0`] [`3.0.0`]: https://github.com/capistrano/capistrano/compare/2.15.5...v3.0.0 If you are coming here to wonder why your Capfile doesn't work anymore, please vendor lock your Capistrano at 2.x, whichever version was working for you until today. Capistrano 3 is a ground-up rewrite with modularity, stability, speed and future proofing in mind. It's a big change, but now the code is 10x smaller, runs faster, is easier to read, and quicker to extend. In the reduction we've come up with a great gem based modular system for plugins and we're really proud of this release. The 3.0.0 release contains 38 patches from the following amazing people: * Tom `seenmyfate` Clements: more than 28 patches including cucumber integration tests! Not to mention Rails asset pipeline code, and bundler integrations. * Lee Hambley: Small changes around compatibility and log formatting * Kir Shatrov: for improvements in the core to make it easier to write extensions, for improving documentation, and for effectively building the chruby, rvm and rbenv integrations. * Michael Nikitochkin: Fixing a bug around linked files and directories. * Jack Thorne: for improvements to the default `Capfile` to fix some bad example syntax. * Erik Hetzner: for (what looks like great) work on the Mercurial (Hg) support. The Hg and Git source control mechanisms do not work the same way, but rather lean on the strengths of the underlying tools. (If I missed anyone, I'm sorry, your contributions have been awesome) The 2.x branch of code is now no longer maintained. Towards the end of it's useful life there were an increasing number of features and pieces of code which didn't make sense for certain groups of people, in certain situations, leading a to a ping-pong tennis effect with pull requests every few weeks "fixing" a use-case which had already been "fixed" shortly before. As many of the use-cases are outside the scope of the testing environments I (and by extension the trusted contributors and IRC regulars) were able to test for. There's a more extensive post about my failure to be able to keep up with the demands of maintaining v2 whilst trying to build something which is appropriate for the current landscape. If you are affected by the unsupported 2 branch, please contact me (Lee Hambley) to discuss how my company can help support you. Otherwise, please try v3, we're sure you'll like it, and the code is designed to be so simple that anyone can work on it. ## `3.0.0.pre14` * Thanks to numerous contributors, in particular (@teohm) for a series of improvements. ## `3.0.0.pre13` * Fixed typos in the Capfile. (@teohm) * Allow setting SSH options globally. (@korin) * Change the flow (and hooks) see http://www.capistranorb.com/documentation/getting-started/flow/ for more information. Requires min SSHKit 0.0.34 (@teohm) * Fix sorting releases in lexicographical order (@teohm) ## `3.0.0.pre12` * `capistrano/bundler` now runs bundle on all roles, this addresses the same issue as the related changes in `pre11`. (@leehambley) ## `3.0.0.pre11` * Some deploy.rake tasks now apply to all servers, not expecting a primary(:app) server which may not exist in all deploy environments. (@leehambley). ## `3.0.0.pre10` * Fixes pre9. ## `3.0.0.pre9` * Fixes a syntax error introduced with filtering (with tests) introduced in `pre8`. (@leehambley) ## `3.0.0.pre8` * Fixed a syntax where `roles(:foo, :bar)` was being mistaken as a filter (roles(:foo, :bar => nil). The correct syntax to use is: roles([:foo,:bar]) (@leehambley) ## `3.0.0.pre7` * Fix Git https authentication. (@leehambley) * Capfile template fixes (repo/repo_url) (@teohm) * Readme Fixes (@ffmike, @kejadlen, @dwickwire) * Fix the positioning of the bundler hook, now immediately after finalize. (@teohm) capistrano-3.10.0/README.md0000644000004100000410000002463013175754131015261 0ustar www-datawww-data # Capistrano: A deployment automation tool built on Ruby, Rake, and SSH. [![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) 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. Once installed, Capistrano gives you a `cap` tool to perform your deployments from the comfort of your command line. ``` $ cd my-capistrano-enabled-project $ cap production deploy ``` When you run `cap`, Capistrano dutifully connects to your server(s) via SSH and executes the steps necessary to deploy your project. You can define those steps yourself by writing [Rake](https://github.com/ruby/rake) tasks, or by using pre-built task libraries provided by the Capistrano community. Tasks are simple to make. Here's an example: ```ruby task :restart_sidekiq do on roles(:worker) do execute :service, "sidekiq restart" end end after "deploy:published", "restart_sidekiq" ``` *Note: This documentation is for the current version of Capistrano (3.x). If you are looking for Capistrano 2.x documentation, you can find it in [this archive](https://github.com/capistrano/capistrano-2.x-docs).* --- ## Contents * [Features](#features) * [Gotchas](#gotchas) * [Quick start](#quick-start) * [Finding help and documentation](#finding-help-and-documentation) * [How to contribute](#how-to-contribute) * [License](#license) ## Features There are many ways to automate deployments, from simple rsync bash scripts to complex containerized toolchains. Capistrano sits somewhere in the middle: it automates what you already know how to do manually with SSH, but in a repeatable, scalable fashion. There is no magic here! Here's what makes Capistrano great: #### Strong conventions Capistrano defines a standard deployment process that all Capistrano-enabled projects follow by default. You don't have to decide how to structure your scripts, where deployed files should be placed on the server, or how to perform common tasks: Capistrano has done this work for you. #### Multiple stages Define your deployment once, and then easily parameterize it for multiple *stages* (environments), e.g. `qa`, `staging`, and `production`. No copy-and-paste necessary: you only need to specify what is different for each stage, like IP addresses. #### Parallel execution Deploying to a fleet of app servers? Capistrano can run each deployment task concurrently across those servers and uses connection pooling for speed. #### Server roles Your application may need many different types of servers: a database server, an app server, two web servers, and a job queue work server, for example. Capistrano lets you tag each server with one or more roles, so you can control what tasks are executed where. #### Community driven Capistrano is easily extensible using the rubygems package manager. Deploying a Rails app? Wordpress? Laravel? Chances are, someone has already written Capistrano tasks for your framework of choice and has distributed it as a gem. Many Ruby projects also come with Capistrano tasks built-in. #### It's just SSH Everything in Capistrano comes down to running SSH commands on remote servers. On the one hand, that makes Capistrano simple. On the other hand, if you aren't comfortable SSH-ing into a Linux box and doing stuff on the command-line, then Capistrano is probably not for you. ## Gotchas While Capistrano ships with a strong set of conventions that are common for all types of deployments, it needs help understanding the specifics of your project, and there are some things Capistrano is not suited to do. #### Project specifics Out of the box, Capistrano can deploy your code to server(s), but it does not know how to *execute* your code. Does `foreman` need to be run? Does Apache need to be restarted? You'll need to tell Capistrano how to do this part by writing these deployment steps yourself, or by finding a gem in the Capistrano community that does it for you. #### Key-based SSH Capistrano depends on connecting to your server(s) with SSH using key-based (i.e. password-less) authentication. You'll need this working before you can use Capistrano. #### Provisioning Likewise, your server(s) will likely need supporting software installed before you can perform a deployment. Capistrano itself has no requirements other than SSH, but your application probably needs database software, a web server like Apache or Nginx, and a language runtime like Java, Ruby, or PHP. These *server provisioning* steps are not done by Capistrano. #### `sudo`, etc. 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. #### Shells Capistrano 3 expects a POSIX shell like Bash or Sh. Shells like tcsh, csh, and such may work, but probably will not. ## Quick start ### Requirements * Ruby version 2.0 or higher on your local machine (MRI or Rubinius) * A project that uses source control (Git, Mercurial, and Subversion support is built-in) * The SCM binaries (e.g. `git`, `hg`) needed to check out your project must be installed on the server(s) you are deploying to * [Bundler](http://bundler.io), along with a Gemfile for your project, are recommended ### Install the Capistrano gem Add Capistrano to your project's Gemfile: ``` ruby group :development do gem "capistrano", "~> 3.10" end ``` Then run Bundler to ensure Capistrano is downloaded and installed: ``` sh $ bundle install ``` ### "Capify" your project Make sure your project doesn't already have a "Capfile" or "capfile" present. Then run: ``` sh $ bundle exec cap install ``` This creates all the necessary configuration files and directory structure for a Capistrano-enabled project with two stages, `staging` and `production`: ``` ├── Capfile ├── config │ ├── deploy │ │ ├── production.rb │ │ └── staging.rb │ └── deploy.rb └── lib └── capistrano └── tasks ``` To customize the stages that are created, use: ``` sh $ bundle exec cap install STAGES=local,sandbox,qa,production ``` Note that the files that Capistrano creates are simply templates to get you started. Make sure to edit the `deploy.rb` and stage files so that they contain values appropriate for your project and your target servers. ### Command-line usage ``` sh # list all available tasks $ bundle exec cap -T # deploy to the staging environment $ bundle exec cap staging deploy # deploy to the production environment $ bundle exec cap production deploy # simulate deploying to the production environment # does not actually do anything $ bundle exec cap production deploy --dry-run # list task dependencies $ bundle exec cap production deploy --prereqs # trace through task invocations $ bundle exec cap production deploy --trace # lists all config variable before deployment tasks $ bundle exec cap production deploy --print-config-variables ``` ## Finding help and documentation Capistrano is a large project encompassing multiple GitHub repositories and a community of plugins, and it can be overwhelming when you are just getting started. Here are resources that can help: * **The [docs](docs) directory contains the official documentation**, and is used to generate the [Capistrano website](http://capistranorb.com) * [Stack Overflow](http://stackoverflow.com/questions/tagged/capistrano) has a Capistrano tag with lots of activity * [The Capistrano mailing list](https://groups.google.com/forum/#!forum/capistrano) is low-traffic but is monitored by Capistrano contributors * [CodersClan](http://codersclan.net/?repo_id=325&source=link) has Capistrano experts available to solve problems for bounties Related GitHub repositories: * [capistrano/sshkit](https://github.com/capistrano/sshkit) provides the SSH behavior that underlies Capistrano (when you use `execute` in a Capistrano task, you are using SSHKit) * [capistrano/rails](https://github.com/capistrano/rails) is a very popular gem that adds Ruby on Rails deployment tasks * [mattbrictson/airbrussh](https://github.com/mattbrictson/airbrussh) provides Capistrano's default log formatting GitHub issues are for bug reports and feature requests. Please refer to the [CONTRIBUTING](CONTRIBUTING.md) document for guidelines on submitting GitHub issues. If you think you may have discovered a security vulnerability in Capistrano, do not open a GitHub issue. Instead, please send a report to . ## How to contribute 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. ## License MIT License (MIT) Copyright (c) 2012-2015 Tom Clements, Lee Hambley Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.