capistrano-3.18.1/0000755000004100000410000000000014600030615013772 5ustar www-datawww-datacapistrano-3.18.1/DEVELOPMENT.md0000644000004100000410000001651714600030615016110 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 documentation 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 ``` ## 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 CI 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.18.1/bin/0000755000004100000410000000000014600030615014542 5ustar www-datawww-datacapistrano-3.18.1/bin/capify0000755000004100000410000000036214600030615015744 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.18.1/bin/cap0000755000004100000410000000011514600030615015230 0ustar www-datawww-data#!/usr/bin/env ruby require "capistrano/all" Capistrano::Application.new.run capistrano-3.18.1/.gitignore0000644000004100000410000000034214600030615015761 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.18.1/CONTRIBUTING.md0000644000004100000410000000671114600030615016230 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.18.1/.github/0000755000004100000410000000000014600030615015332 5ustar www-datawww-datacapistrano-3.18.1/.github/pull_request_template.md0000644000004100000410000000134014600030615022271 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? ### Other Information If there's anything else that's important and relevant to your pull request, mention that information here. Thanks for helping improve Capistrano! capistrano-3.18.1/.github/release-drafter.yml0000644000004100000410000000126114600030615021122 0ustar www-datawww-dataname-template: "$RESOLVED_VERSION" tag-template: "v$RESOLVED_VERSION" categories: - title: "⚠️ Breaking Changes" label: "⚠️ Breaking" - title: "✨ New Features" label: "✨ Feature" - title: "🐛 Bug Fixes" label: "🐛 Bug Fix" - title: "📚 Documentation" label: "📚 Docs" - title: "🏠 Housekeeping" label: "🏠 Housekeeping" version-resolver: minor: labels: - "⚠️ Breaking" - "✨ Feature" default: patch change-template: "- $TITLE (#$NUMBER) @$AUTHOR" no-changes-template: "- No changes" template: | $CHANGES **Full Changelog:** https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION capistrano-3.18.1/.github/workflows/0000755000004100000410000000000014600030615017367 5ustar www-datawww-datacapistrano-3.18.1/.github/workflows/release-drafter.yml0000644000004100000410000000045514600030615023163 0ustar www-datawww-dataname: Release Drafter on: push: branches: - master permissions: contents: write pull-requests: read jobs: update_release_draft: runs-on: ubuntu-latest steps: - uses: release-drafter/release-drafter@v5 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} capistrano-3.18.1/.github/workflows/ci.yml0000644000004100000410000000473014600030615020511 0ustar www-datawww-dataname: CI on: push: branches: [master] pull_request: jobs: spec: runs-on: ubuntu-latest strategy: matrix: ruby: [ "2.3", "2.4", "2.5", "2.6", "2.7", "3.0", "3.1", "3.2", "3.3", "head", ] steps: - uses: actions/checkout@v4 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} bundler-cache: true - name: rake spec run: bundle exec rake spec spec-legacy: runs-on: ubuntu-20.04 strategy: matrix: ruby: ["2.0", "2.1", "2.2"] steps: - uses: actions/checkout@v4 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} bundler-cache: true - name: rake spec run: bundle exec rake spec spec-all: runs-on: ubuntu-latest needs: [spec, spec-legacy] if: always() steps: - name: All tests ok if: ${{ !(contains(needs.*.result, 'failure')) }} run: exit 0 - name: Some tests failed if: ${{ contains(needs.*.result, 'failure') }} run: exit 1 rubocop: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: ruby-version: "ruby" # latest-stable bundler-cache: true - name: rake rubocop run: bundle exec rake rubocop danger: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: ruby-version: "ruby" # latest-stable bundler-cache: true - name: danger env: DANGER_GITHUB_API_TOKEN: ${{ secrets.DANGER_GITHUB_API_TOKEN }} run: bundle exec danger features: needs: [spec, spec-legacy] runs-on: macos-12 steps: - uses: actions/checkout@v4 - name: Cache Vagrant boxes uses: actions/cache@v3 with: path: ~/.vagrant.d/boxes key: ${{ runner.os }}-vagrant-${{ hashFiles('Vagrantfile') }} restore-keys: | ${{ runner.os }}-vagrant- - name: Set up Ruby uses: ruby/setup-ruby@v1 with: ruby-version: "ruby" # latest-stable bundler-cache: true - name: rake features run: bundle exec rake features capistrano-3.18.1/.github/issue_template.md0000644000004100000410000000203414600030615020676 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.18.1/lib/0000755000004100000410000000000014600030615014540 5ustar www-datawww-datacapistrano-3.18.1/lib/capistrano.rb0000644000004100000410000000000014600030615017216 0ustar www-datawww-datacapistrano-3.18.1/lib/capistrano/0000755000004100000410000000000014600030615016703 5ustar www-datawww-datacapistrano-3.18.1/lib/capistrano/scm/0000755000004100000410000000000014600030615017465 5ustar www-datawww-datacapistrano-3.18.1/lib/capistrano/scm/tasks/0000755000004100000410000000000014600030615020612 5ustar www-datawww-datacapistrano-3.18.1/lib/capistrano/scm/tasks/hg.rake0000644000004100000410000000222614600030615022056 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.18.1/lib/capistrano/scm/tasks/git.rake0000644000004100000410000000520714600030615022245 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), in: :groups, limit: fetch(:git_max_concurrent_connections), wait: fetch(:git_wait_interval) do execute :mkdir, "-p", File.dirname(fetch(:git_wrapper_path)).shellescape upload! StringIO.new("#!/bin/sh -e\nexec /usr/bin/env 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), in: :groups, limit: fetch(:git_max_concurrent_connections), wait: fetch(:git_wait_interval) 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), in: :groups, limit: fetch(:git_max_concurrent_connections), wait: fetch(:git_wait_interval) 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), in: :groups, limit: fetch(:git_max_concurrent_connections), wait: fetch(:git_wait_interval) do within repo_path do with fetch(:git_environmental_variables) do git_plugin.update_mirror git_plugin.verify_commit if fetch(:git_verify_commit) end end end end desc "Copy repo to releases" task create_release: :'git:update' do on release_roles(:all), in: :groups, limit: fetch(:git_max_concurrent_connections), wait: fetch(:git_wait_interval) 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), in: :groups, limit: fetch(:git_max_concurrent_connections), wait: fetch(:git_wait_interval) 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.18.1/lib/capistrano/scm/tasks/svn.rake0000644000004100000410000000224114600030615022263 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.18.1/lib/capistrano/scm/hg.rb0000644000004100000410000000254314600030615020414 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.18.1/lib/capistrano/scm/plugin.rb0000644000004100000410000000064714600030615021317 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.18.1/lib/capistrano/scm/git.rb0000644000004100000410000000543614600030615020605 0ustar www-datawww-datarequire "capistrano/scm/plugin" require "cgi" require "securerandom" 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 { # Use a unique name that won't collide with other deployments, and # that cannot be guessed by other processes that have access to /tmp. "#{fetch(:tmp_dir)}/git-ssh-#{SecureRandom.hex(10)}.sh" } set_if_empty :git_environmental_variables, lambda { { git_askpass: "/bin/echo", git_ssh: fetch(:git_wrapper_path) } } set_if_empty :git_max_concurrent_connections, 10 set_if_empty :git_wait_interval, 0 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 verify_commit git :"verify-commit", fetch_revision 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.18.1/lib/capistrano/scm/svn.rb0000644000004100000410000000310414600030615020616 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.18.1/lib/capistrano/doctor/0000755000004100000410000000000014600030615020175 5ustar www-datawww-datacapistrano-3.18.1/lib/capistrano/doctor/variables_doctor.rb0000644000004100000410000000361214600030615024046 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 linked_dirs linked_files 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.18.1/lib/capistrano/doctor/output_helpers.rb0000644000004100000410000000433214600030615023606 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.18.1/lib/capistrano/doctor/servers_doctor.rb0000644000004100000410000000471314600030615023572 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.18.1/lib/capistrano/doctor/gems_doctor.rb0000644000004100000410000000225114600030615023027 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.18.1/lib/capistrano/doctor/environment_doctor.rb0000644000004100000410000000074014600030615024441 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.18.1/lib/capistrano/dsl.rb0000644000004100000410000000622214600030615020014 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.18.1/lib/capistrano/immutable_task.rb0000644000004100000410000000170414600030615022233 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.18.1/lib/capistrano/dsl/0000755000004100000410000000000014600030615017465 5ustar www-datawww-datacapistrano-3.18.1/lib/capistrano/dsl/paths.rb0000644000004100000410000000356414600030615021141 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.18.1/lib/capistrano/dsl/stages.rb0000644000004100000410000000133214600030615021277 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.18.1/lib/capistrano/dsl/env.rb0000644000004100000410000000201514600030615020600 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.18.1/lib/capistrano/dsl/task_enhancements.rb0000644000004100000410000000306014600030615023503 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.18.1/lib/capistrano/upload_task.rb0000644000004100000410000000030214600030615021531 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.18.1/lib/capistrano/version_validator.rb0000644000004100000410000000102014600030615022753 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.18.1/lib/capistrano/configuration.rb0000644000004100000410000001153614600030615022105 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?) begin return value.any? rescue ArgumentError # rubocop:disable Lint/HandleExceptions # Gracefully ignore values whose `any?` method doesn't accept 0 args end end !value.nil? 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.18.1/lib/capistrano/templates/0000755000004100000410000000000014600030615020701 5ustar www-datawww-datacapistrano-3.18.1/lib/capistrano/templates/deploy.rb.erb0000644000004100000410000000246514600030615023300 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/master.key' # Default value for linked_dirs is [] # append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "public/system", "vendor", "storage" # 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.18.1/lib/capistrano/templates/Capfile0000644000004100000410000000211614600030615022167 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.18.1/lib/capistrano/templates/stage.rb.erb0000644000004100000410000000405014600030615023077 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/user_name/.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.18.1/lib/capistrano/tasks/0000755000004100000410000000000014600030615020030 5ustar www-datawww-datacapistrano-3.18.1/lib/capistrano/tasks/console.rake0000644000004100000410000000074614600030615022345 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.18.1/lib/capistrano/tasks/install.rake0000644000004100000410000000242314600030615022343 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.18.1/lib/capistrano/tasks/doctor.rake0000644000004100000410000000124314600030615022166 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.18.1/lib/capistrano/tasks/framework.rake0000644000004100000410000000225414600030615022674 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.18.1/lib/capistrano/tasks/deploy.rake0000644000004100000410000001663014600030615022176 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.each_slice(100) do |directories_batch| execute :rm, "-rf", *directories_batch end 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.18.1/lib/capistrano/doctor.rb0000644000004100000410000000035014600030615020520 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.18.1/lib/capistrano/scm.rb0000644000004100000410000000537214600030615020021 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.18.1/lib/capistrano/configuration/0000755000004100000410000000000014600030615021552 5ustar www-datawww-datacapistrano-3.18.1/lib/capistrano/configuration/empty_filter.rb0000644000004100000410000000020014600030615024572 0ustar www-datawww-datamodule Capistrano class Configuration class EmptyFilter def filter(_servers) [] end end end end capistrano-3.18.1/lib/capistrano/configuration/host_filter.rb0000644000004100000410000000126714600030615024427 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-z0-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.18.1/lib/capistrano/configuration/server.rb0000644000004100000410000000555014600030615023412 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.18.1/lib/capistrano/configuration/filter.rb0000644000004100000410000000154114600030615023365 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.18.1/lib/capistrano/configuration/role_filter.rb0000644000004100000410000000125314600030615024406 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.18.1/lib/capistrano/configuration/servers.rb0000644000004100000410000000422614600030615023574 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.18.1/lib/capistrano/configuration/scm_resolver.rb0000644000004100000410000001116714600030615024610 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.18.1/lib/capistrano/configuration/validated_variables.rb0000644000004100000410000000670614600030615026075 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 = proc 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.18.1/lib/capistrano/configuration/plugin_installer.rb0000644000004100000410000000311414600030615025451 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.18.1/lib/capistrano/configuration/variables.rb0000644000004100000410000000554014600030615024053 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.18.1/lib/capistrano/configuration/null_filter.rb0000644000004100000410000000020314600030615024411 0ustar www-datawww-datamodule Capistrano class Configuration class NullFilter def filter(servers) servers end end end end capistrano-3.18.1/lib/capistrano/configuration/question.rb0000644000004100000410000000306614600030615023753 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 return unless stdin.tty? 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 prompt && default.nil? I18n.t(:question_prompt, key: prompt, scope: :capistrano) elsif prompt I18n.t(:question_prompt_default, key: prompt, default_value: default, scope: :capistrano) elsif 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 def stdin (options || {}).fetch(:stdin, $stdin) end def prompt (options || {}).fetch(:prompt, nil) end end end end capistrano-3.18.1/lib/capistrano/deploy.rb0000644000004100000410000000013014600030615020516 0ustar www-datawww-datarequire "capistrano/framework" load File.expand_path("../tasks/deploy.rake", __FILE__) capistrano-3.18.1/lib/capistrano/plugin.rb0000644000004100000410000000555514600030615020540 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.18.1/lib/capistrano/defaults.rb0000644000004100000410000000232314600030615021037 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.18.1/lib/capistrano/install.rb0000644000004100000410000000011714600030615020675 0ustar www-datawww-dataload File.expand_path(File.join(File.dirname(__FILE__), "tasks/install.rake")) capistrano-3.18.1/lib/capistrano/application.rb0000644000004100000410000001002014600030615021524 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.18.1/lib/capistrano/all.rb0000644000004100000410000000054014600030615017777 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.18.1/lib/capistrano/dotfile.rb0000644000004100000410000000012614600030615020655 0ustar www-datawww-datadotfile = Pathname.new(File.join(Dir.home, ".capfile")) load dotfile if dotfile.file? capistrano-3.18.1/lib/capistrano/proc_helpers.rb0000644000004100000410000000070114600030615021713 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.18.1/lib/capistrano/version.rb0000644000004100000410000000006214600030615020713 0ustar www-datawww-datamodule Capistrano VERSION = "3.18.1".freeze end capistrano-3.18.1/lib/capistrano/setup.rb0000644000004100000410000000166714600030615020402 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.18.1/lib/capistrano/console.rb0000644000004100000410000000007114600030615020670 0ustar www-datawww-dataload File.expand_path("../tasks/console.rake", __FILE__) capistrano-3.18.1/lib/capistrano/i18n.rb0000644000004100000410000000402514600030615020010 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}): ", question_prompt: "%{key}: ", question_prompt_default: "%{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.18.1/lib/capistrano/framework.rb0000644000004100000410000000013014600030615021217 0ustar www-datawww-dataload File.expand_path("../tasks/framework.rake", __FILE__) require "capistrano/install" capistrano-3.18.1/lib/Capfile0000644000004100000410000000011014600030615016016 0ustar www-datawww-data#!/usr/bin/env cap include Capistrano::DSL require "capistrano/install" capistrano-3.18.1/RELEASING.md0000644000004100000410000000170114600030615015624 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`. 5. Update the version in the `./README.md` Gemfile example (`gem "capistrano", "~> X.Y"`). 6. Commit the `version.rb` and `README.md` changes in a single commit, the message should be "Release vX.Y.Z" 7. Run `rake release`; this will tag, push to GitHub, and publish to rubygems.org. 8. Update the draft release on the [GitHub releases page](https://github.com/capistrano/capistrano/releases) to point to the new tag and publish the release capistrano-3.18.1/capistrano.gemspec0000644000004100000410000000266214600030615017510 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 = "https://capistranorb.com/" gem.metadata = { "bug_tracker_uri" => "https://github.com/capistrano/capistrano/issues", "changelog_uri" => "https://github.com/capistrano/capistrano/releases", "source_code_uri" => "https://github.com/capistrano/capistrano", "homepage_uri" => "https://capistranorb.com/", "documentation_uri" => "https://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" end capistrano-3.18.1/LICENSE.txt0000644000004100000410000000210514600030615015613 0ustar www-datawww-dataMIT License (MIT) Copyright (c) 2012-2020 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.18.1/spec/0000755000004100000410000000000014600030615014724 5ustar www-datawww-datacapistrano-3.18.1/spec/integration/0000755000004100000410000000000014600030615017247 5ustar www-datawww-datacapistrano-3.18.1/spec/integration/dsl_spec.rb0000644000004100000410000005061714600030615021401 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 explicitly 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 let(:stdin) { stub(tty?: true) } before do dsl.ask(:scm, :svn, stdin: stdin) $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.18.1/spec/spec_helper.rb0000644000004100000410000000147014600030615017544 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" config.around(:example, capture_io: true) do |example| begin Rake.application.options.trace_output = StringIO.new $stdout = StringIO.new $stderr = StringIO.new example.run ensure Rake.application.options.trace_output = STDERR $stdout = STDOUT $stderr = STDERR end end end capistrano-3.18.1/spec/lib/0000755000004100000410000000000014600030615015472 5ustar www-datawww-datacapistrano-3.18.1/spec/lib/capistrano_spec.rb0000644000004100000410000000015714600030615021177 0ustar www-datawww-datarequire "spec_helper" module Capistrano describe Application do let(:app) { Application.new } end end capistrano-3.18.1/spec/lib/capistrano/0000755000004100000410000000000014600030615017635 5ustar www-datawww-datacapistrano-3.18.1/spec/lib/capistrano/scm/0000755000004100000410000000000014600030615020417 5ustar www-datawww-datacapistrano-3.18.1/spec/lib/capistrano/scm/hg_spec.rb0000644000004100000410000000626614600030615022366 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.18.1/spec/lib/capistrano/scm/svn_spec.rb0000644000004100000410000001064614600030615022573 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.18.1/spec/lib/capistrano/scm/git_spec.rb0000644000004100000410000001353414600030615022547 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 a random hex value" do env.set(:tmp_dir, "/tmp") subject.set_defaults expect(env.fetch(:git_wrapper_path)).to match(%r{/tmp/git-ssh-\h{20}\.sh}) end it "makes git_max_concurrent_connections" do subject.set_defaults expect(env.fetch(:git_max_concurrent_connections)).to eq(10) env.set(:git_max_concurrent_connections, 7) expect(env.fetch(:git_max_concurrent_connections)).to eq(7) end it "makes git_wait_interval" do subject.set_defaults expect(env.fetch(:git_wait_interval)).to eq(0) env.set(:git_wait_interval, 5) expect(env.fetch(:git_wait_interval)).to eq(5) 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 describe "#verify_commit" do it "should run git verify-commit" do env.set(:branch, "branch") backend.expects(:capture).with(:git, "rev-list --max-count=1 branch").returns("81cec13b777ff46348693d327fc8e7832f79bf43") backend.expects(:execute).with(:git, :"verify-commit", "81cec13b777ff46348693d327fc8e7832f79bf43") subject.verify_commit end end end end capistrano-3.18.1/spec/lib/capistrano/doctor/0000755000004100000410000000000014600030615021127 5ustar www-datawww-datacapistrano-3.18.1/spec/lib/capistrano/doctor/servers_doctor_spec.rb0000644000004100000410000000510514600030615025532 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", capture_io: true 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.18.1/spec/lib/capistrano/doctor/environment_doctor_spec.rb0000644000004100000410000000231314600030615026403 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", capture_io: true 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.18.1/spec/lib/capistrano/doctor/output_helpers_spec.rb0000644000004100000410000000252614600030615025555 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.18.1/spec/lib/capistrano/doctor/gems_doctor_spec.rb0000644000004100000410000000365014600030615024777 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", capture_io: true 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.18.1/spec/lib/capistrano/doctor/variables_doctor_spec.rb0000644000004100000410000000540114600030615026010 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", capture_io: true 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.18.1/spec/lib/capistrano/dsl_spec.rb0000644000004100000410000000552514600030615021765 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", capture_io: true 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 re-enable invoking task", capture_io: true 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", capture_io: true 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 re-enable invoking task", capture_io: true 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", capture_io: true 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.18.1/spec/lib/capistrano/dsl/0000755000004100000410000000000014600030615020417 5ustar www-datawww-datacapistrano-3.18.1/spec/lib/capistrano/dsl/paths_spec.rb0000644000004100000410000001302014600030615023071 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.18.1/spec/lib/capistrano/dsl/task_enhancements_spec.rb0000644000004100000410000000615714600030615025461 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", capture_io: true 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", capture_io: true 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", capture_io: true 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", capture_io: true 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", capture_io: true 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", capture_io: true 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.18.1/spec/lib/capistrano/configuration/0000755000004100000410000000000014600030615022504 5ustar www-datawww-datacapistrano-3.18.1/spec/lib/capistrano/configuration/server_spec.rb0000644000004100000410000002137314600030615025357 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.18.1/spec/lib/capistrano/configuration/filter_spec.rb0000644000004100000410000000666214600030615025342 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.18.1/spec/lib/capistrano/configuration/empty_filter_spec.rb0000644000004100000410000000053414600030615026550 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.18.1/spec/lib/capistrano/configuration/host_filter_spec.rb0000644000004100000410000000433414600030615026371 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("server10")] 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,server10" } it_behaves_like "it filters hosts correctly", %w{server1 server10} 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 server10} 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 server10} end context "without number" do let(:values) { "server" } it_behaves_like "it filters hosts correctly", %w{} end end end end end capistrano-3.18.1/spec/lib/capistrano/configuration/question_spec.rb0000644000004100000410000000601114600030615025710 0ustar www-datawww-datarequire "spec_helper" module Capistrano class Configuration describe Question do let(:question) { Question.new(key, default, stdin: stdin) } let(:question_without_echo) { Question.new(key, default, echo: false, stdin: stdin) } let(:question_without_default) { Question.new(key, nil, stdin: stdin) } let(:question_prompt) { Question.new(key, default, stdin: stdin, prompt: "Your favorite branch") } let(:question_prompt_without_default) { Question.new(key, nil, stdin: stdin, prompt: "Your favorite branch") } let(:default) { :default } let(:key) { :branch } let(:stdin) { stub(tty?: true) } 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 it "uses prompt and returns the value" do $stdout.expects(:print).with("Your favorite branch (default): ") stdin.expects(:gets).returns(branch) stdin.expects(:noecho).never expect(question_prompt.call).to eq(branch) end it "uses prompt and returns the value but has no default between parenthesis" do $stdout.expects(:print).with("Your favorite branch: ") stdin.expects(:gets).returns(branch) stdin.expects(:noecho).never expect(question_prompt_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 context "tty unavailable", capture_io: true do before do stdin.expects(:gets).never stdin.expects(:tty?).returns(false) end it "returns the default as the value" do expect(question.call).to eq(default) end end end end end end capistrano-3.18.1/spec/lib/capistrano/configuration/servers_spec.rb0000644000004100000410000003022214600030615025533 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.18.1/spec/lib/capistrano/configuration/null_filter_spec.rb0000644000004100000410000000056114600030615026364 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.18.1/spec/lib/capistrano/configuration/role_filter_spec.rb0000644000004100000410000000500714600030615026353 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.18.1/spec/lib/capistrano/configuration/scm_resolver_spec.rb0000644000004100000410000000253714600030615026555 0ustar www-datawww-datarequire "spec_helper" require "capistrano/scm" 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", capture_io: true do resolver.resolve expect(Rake::Task["git:wrapper"]).not_to be_nil end it "sets :scm to :git", capture_io: true 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.18.1/spec/lib/capistrano/configuration/plugin_installer_spec.rb0000644000004100000410000000471414600030615027424 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", capture_io: true 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.18.1/spec/lib/capistrano/upload_task_spec.rb0000644000004100000410000000107114600030615023501 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.18.1/spec/lib/capistrano/plugin_spec.rb0000644000004100000410000000426114600030615022475 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", capture_io: true 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", capture_io: true 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.18.1/spec/lib/capistrano/immutable_task_spec.rb0000644000004100000410000000144514600030615024201 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.18.1/spec/lib/capistrano/scm_spec.rb0000644000004100000410000000455114600030615021763 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.18.1/spec/lib/capistrano/version_validator_spec.rb0000644000004100000410000000547214600030615024736 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.18.1/spec/lib/capistrano/application_spec.rb0000644000004100000410000000355414600030615023506 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" it "displays documentation URL as help banner", capture_io: true do flags "--help", "-h" expect($stdout.string.each_line.first).to match(/capistranorb.com/) end %w(quiet silent verbose).each do |switch| it "doesn't include --#{switch} in help", capture_io: true do flags "--help", "-h" expect($stdout.string).not_to match(/--#{switch}/) end end it "overrides the rake method, but still prints the rake version", capture_io: true do flags "--version", "-V" out = $stdout.string 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", capture_io: true do flags "--dry-run", "-n" 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", capture_io: true do begin flags "--print-config-variables", "-p" expect(Capistrano::Configuration.fetch(:print_config_variables)).to be true ensure Capistrano::Configuration.reset! end 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 end capistrano-3.18.1/spec/lib/capistrano/configuration_spec.rb0000644000004100000410000002361214600030615024047 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.18.1/spec/support/0000755000004100000410000000000014600030615016440 5ustar www-datawww-datacapistrano-3.18.1/spec/support/.gitignore0000644000004100000410000000001114600030615020420 0ustar www-datawww-data.vagrant capistrano-3.18.1/spec/support/tasks/0000755000004100000410000000000014600030615017565 5ustar www-datawww-datacapistrano-3.18.1/spec/support/tasks/plugin.rake0000644000004100000410000000021514600030615021725 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.18.1/spec/support/tasks/fail.rake0000644000004100000410000000027414600030615021347 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.18.1/spec/support/tasks/root.rake0000644000004100000410000000033714600030615021417 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.18.1/spec/support/tasks/database.rake0000644000004100000410000000035014600030615022173 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.18.1/spec/support/tasks/failed.rake0000644000004100000410000000016514600030615021657 0ustar www-datawww-dataafter "deploy:failed", :custom_failed do on roles :all do execute :touch, shared_path.join("failed") end end capistrano-3.18.1/spec/support/matchers.rb0000644000004100000410000000017214600030615020573 0ustar www-datawww-dataRSpec::Matchers.define :be_a_symlink_to do |expected| match do |actual| File.identical?(expected, actual) end end capistrano-3.18.1/spec/support/test_app.rb0000644000004100000410000001020114600030615020576 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, 'https://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 "source 'https://rubygems.org'\n" 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_glob "/tmp/git-ssh-*.sh" end def with_clean_bundler_env(&block) return yield unless defined?(Bundler) if Bundler.respond_to?(:with_unbundled_env) Bundler.with_unbundled_env(&block) else Bundler.with_clean_env(&block) end end end capistrano-3.18.1/spec/support/Vagrantfile0000644000004100000410000000142614600030615020630 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/bionic64" 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 = URI.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.18.1/spec/integration_spec_helper.rb0000644000004100000410000000013514600030615022144 0ustar www-datawww-datarequire "spec_helper" require "support/test_app" require "support/matchers" include TestApp capistrano-3.18.1/Rakefile0000644000004100000410000000073414600030615015443 0ustar www-datawww-datarequire "bundler/gem_tasks" require "cucumber/rake/task" require "rspec/core/rake_task" begin require "rubocop/rake_task" desc "Run RuboCop checks" RuboCop::RakeTask.new task default: %i(spec rubocop) rescue LoadError task default: :spec end RSpec::Core::RakeTask.new Cucumber::Rake::Task.new(:features) Rake::Task["release"].enhance do puts "Don't forget to publish the release on GitHub!" system "open https://github.com/capistrano/capistrano/releases" end capistrano-3.18.1/features/0000755000004100000410000000000014600030615015610 5ustar www-datawww-datacapistrano-3.18.1/features/step_definitions/0000755000004100000410000000000014600030615021156 5ustar www-datawww-datacapistrano-3.18.1/features/step_definitions/assertions.rb0000644000004100000410000001106514600030615023700 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_glob}) == "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.18.1/features/step_definitions/setup.rb0000644000004100000410000000520614600030615022646 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 (1...num).each_slice(100) do |num_batch| dirs = num_batch.map do |i| offset = -(a_day * i) TestApp.release_path(TestApp.timestamp(offset)) end run_vagrant_command("mkdir -p #{dirs.join(' ')}") end end Given(/^an invalid release named "(.+)"$/) do |filename| run_vagrant_command("mkdir -p #{TestApp.release_path(filename)}") end capistrano-3.18.1/features/step_definitions/cap_commands.rb0000644000004100000410000000113214600030615024124 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.18.1/features/deploy.feature0000644000004100000410000000616214600030615020466 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: Cleanup when there are more releases than arguments can handle Given config stage file has line "set :keep_releases, 3" And 5000 valid existing releases When I run cap "deploy:cleanup" Then 3 valid releases are kept 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.18.1/features/stage_failure.feature0000644000004100000410000000031714600030615022000 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.18.1/features/deploy_failure.feature0000644000004100000410000000100114600030615022160 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.18.1/features/sshconnect.feature0000644000004100000410000000060114600030615021331 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.18.1/features/configuration.feature0000644000004100000410000000200714600030615022033 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.18.1/features/doctor.feature0000644000004100000410000000046214600030615020461 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.18.1/features/installation.feature0000644000004100000410000000117014600030615021665 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.18.1/features/support/0000755000004100000410000000000014600030615017324 5ustar www-datawww-datacapistrano-3.18.1/features/support/remote_command_helpers.rb0000644000004100000410000000102514600030615024362 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.18.1/features/support/env.rb0000644000004100000410000000047614600030615020450 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.18.1/features/support/vagrant_helpers.rb0000644000004100000410000000210014600030615023026 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 def puts(message) # Attach log messages to the current cucumber feature (`log`), # or simply puts to the console (`super`) if we are outside of cucumber. respond_to?(:log) ? log(message) : super(message) end end World(VagrantHelpers) capistrano-3.18.1/features/subdirectory.feature0000644000004100000410000000050514600030615021703 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.18.1/Gemfile0000644000004100000410000000261114600030615015265 0ustar www-datawww-datasource "https://rubygems.org" # Specify your gem's dependencies in capistrano.gemspec gemspec gem "mocha" gem "rspec" gem "rspec-core", "~> 3.4.4" group :cucumber do # Latest versions of cucumber don't support Ruby < 2.1 # rubocop:disable Bundler/DuplicatedGem if Gem::Requirement.new("< 2.1").satisfied_by?(Gem::Version.new(RUBY_VERSION)) gem "cucumber", "< 3.0.1" else gem "cucumber" end # rubocop:enable Bundler/DuplicatedGem end # Latest versions of net-ssh don't support Ruby < 2.2.6 if Gem::Requirement.new("< 2.2.6").satisfied_by?(Gem::Version.new(RUBY_VERSION)) gem "net-ssh", "< 5.0.0" end # Latest versions of public_suffix don't support Ruby < 2.1 if Gem::Requirement.new("< 2.1").satisfied_by?(Gem::Version.new(RUBY_VERSION)) gem "public_suffix", "< 3.0.0" end # Latest versions of i18n don't support Ruby < 2.4 if Gem::Requirement.new("< 2.4").satisfied_by?(Gem::Version.new(RUBY_VERSION)) gem "i18n", "< 1.3.0" end # Latest versions of rake don't support Ruby < 2.2 if Gem::Requirement.new("< 2.2").satisfied_by?(Gem::Version.new(RUBY_VERSION)) gem "rake", "< 13.0.0" end # We only run danger and rubocop on a new-ish ruby; no need to install them otherwise if Gem::Requirement.new("> 2.4").satisfied_by?(Gem::Version.new(RUBY_VERSION)) gem "base64" gem "danger" gem "psych", "< 4" # Ensures rubocop works on Ruby 3.1 gem "racc" gem "rubocop", "0.48.1" end capistrano-3.18.1/README.md0000644000004100000410000002474314600030615015263 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://github.com/capistrano/capistrano/actions/workflows/ci.yml/badge.svg)](https://github.com/capistrano/capistrano/actions/workflows/ci.yml) [![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 using `require: false`: ``` ruby group :development do gem "capistrano", "~> 3.17", require: false 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-2020 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.18.1/.rubocop.yml0000644000004100000410000000231514600030615016245 0ustar www-datawww-dataAllCops: DisplayCopNames: true DisplayStyleGuide: true TargetRubyVersion: 2.0 Lint/AmbiguousBlockAssociation: Enabled: false Metrics/BlockLength: Exclude: - "*.gemspec" - "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.18.1/CHANGELOG.md0000644000004100000410000000014014600030615015576 0ustar www-datawww-dataRelease notes for this project are kept here: https://github.com/capistrano/capistrano/releases capistrano-3.18.1/Dangerfile0000644000004100000410000000011614600030615015753 0ustar www-datawww-datadanger.import_dangerfile(github: "capistrano/danger", branch: "no-changelog") capistrano-3.18.1/UPGRADING-3.7.md0000644000004100000410000000501714600030615016144 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.