require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper.rb')) require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'whiskey_disk')) require 'rake' # @whiskey_disk subclass that allows us to test in what order ssh commands are # issued by @whiskey_disk.run class TestOrderedExecution < WhiskeyDisk class << self def commands result = @commands @commands = [] result end def system(*args) @commands ||= [] @commands << args.join(' ') end end end describe 'requiring the main library' do before do Rake.application = @rake = Rake::Application.new load File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'whiskey_disk', 'rake.rb')) end after do Rake.application = nil end it 'makes the deploy:setup rake task available' do Rake::Task.task_defined?('deploy:setup').should.be.true end it 'makes the deploy:now rake task available' do Rake::Task.task_defined?('deploy:now').should.be.true end end describe '@whiskey_disk' do describe 'when initializing' do it 'works without arguments' do lambda { WhiskeyDisk.new }.should.not.raise(ArgumentError) end it 'works with an options hash' do lambda { WhiskeyDisk.new(:foo => 'bar') }.should.not.raise(ArgumentError) end it 'saves the staleness checks state if enabled via the options hash' do @whiskey_disk = WhiskeyDisk.new(:staleness_checks => true) @whiskey_disk.staleness_checks_enabled?.should == true end it 'leaves staleness checks disabled if not enabled via the options hash' do @whiskey_disk = WhiskeyDisk.new @whiskey_disk.staleness_checks_enabled?.should == false end it 'leaves staleness checks disabled if disabled via the options hash' do @whiskey_disk = WhiskeyDisk.new(:staleness_checks => false) @whiskey_disk.staleness_checks_enabled?.should == false end end before do @whiskey_disk = WhiskeyDisk.new end describe 'determining if the deployment is remote' do before do @parameters = { 'deploy_to' => '/path/to/main/repo' } @whiskey_disk.configuration = @parameters end it 'allows a domain argument' do lambda { @whiskey_disk.remote?('domain') }.should.not.raise(ArgumentError) end it 'requires a domain argument' do lambda { @whiskey_disk.remote? }.should.raise(ArgumentError) end describe 'when a domain limit is specified in the configuration' do before do @domain = 'myhost' @config = WhiskeyDisk::Config.new @config.stub!(:domain_limit).and_return(@domain) @whiskey_disk.config = @config end it 'returns false if the provided domain is nil' do @whiskey_disk.remote?(nil).should == false end it 'returns false if the provided domain is the string "local"' do @whiskey_disk.remote?('local').should == false end it 'returns false if the provided domain matches the limit domain from the configuration' do @whiskey_disk.remote?(@domain).should == false end it 'returns false if the provided domain, ignoring any user@, matches the limit domain from the configuration' do @whiskey_disk.remote?("user@" + @domain).should == false end it 'returns true if the provided domain does not match the limit domain from the configuration' do @whiskey_disk.remote?('smeghost').should == true end end describe 'when no domain limit is specified in the configuration' do before do @config = WhiskeyDisk::Config.new @config.stub!(:domain_limit).and_return(nil) @whiskey_disk.config = @config end it 'returns false if the provided domain is nil' do @whiskey_disk.remote?(nil).should == false end it 'returns false if the provided domain is the string "local"' do @whiskey_disk.remote?('local').should == false end it 'returns true if the provided domain is non-empty' do @whiskey_disk.remote?('smeghost').should == true end end end describe 'determining if the deployment has a configuration repository' do before do @parameters = { 'deploy_to' => '/path/to/main/repo' } @whiskey_disk.configuration = @parameters end it 'works without arguments' do lambda { @whiskey_disk.has_config_repo? }.should.not.raise(ArgumentError) end it 'does not allow arguments' do lambda { @whiskey_disk.has_config_repo?(:foo) }.should.raise(ArgumentError) end it 'returns true if the configuration includes a non-empty config_repository setting' do @parameters['config_repository'] = 'git://foo.git' @whiskey_disk.has_config_repo?.should == true end it 'returns false if the configuration includes a nil config_repository setting' do @parameters['config_repository'] = nil @whiskey_disk.has_config_repo?.should == false end it 'returns false if the configuration includes a blank config_repository setting' do @parameters['config_repository'] = '' @whiskey_disk.has_config_repo?.should == false end end describe 'when checking staleness checks' do it 'returns false if staleness checks have not been enabled' do @whiskey_disk.staleness_checks_enabled?.should == false end it 'returns true if staleness checks have been enabled' do @whiskey_disk = WhiskeyDisk.new(:staleness_checks => true) @whiskey_disk.staleness_checks_enabled?.should == true end end describe 'ensuring that the parent path for the main repository checkout is present' do before do @whiskey_disk.configuration = { 'deploy_to' => '/path/to/main/repo' } end it 'fails if the deployment path is not specified' do @whiskey_disk.configuration = {} lambda { @whiskey_disk.ensure_main_parent_path_is_present }.should.raise end it 'attempts to create the parent path for the repository' do @whiskey_disk.ensure_main_parent_path_is_present @whiskey_disk.buffer.last.should.match(%r{mkdir -p /path/to/main}) @whiskey_disk.buffer.last.should.not.match(%r{/path/to/main/repo}) end end describe 'ensuring that the parent path for the configuration repository checkout is present' do before do @whiskey_disk.configuration = { 'deploy_config_to' => '/path/to/config/repo' } end it 'fails if the configuration deployment path is not specified' do @whiskey_disk.configuration = {} lambda { @whiskey_disk.ensure_config_parent_path_is_present }.should.raise end it 'attempts to create the parent path for the repository' do @whiskey_disk.ensure_config_parent_path_is_present @whiskey_disk.buffer.last.should.match(%r{mkdir -p /path/to/config}) @whiskey_disk.buffer.last.should.not.match(%r{/path/to/config/repo}) end end describe 'checking out the main repository' do before do @parameters = { 'deploy_to' => '/path/to/main/repo', 'repository' => 'git@ogtastic.com:whiskey_disk.git' } @whiskey_disk.configuration = @parameters end it 'fails if the deployment path is not specified' do @whiskey_disk.configuration = @parameters.merge('deploy_to' => nil) lambda { @whiskey_disk.checkout_main_repository }.should.raise end it 'fails if the repository is not specified' do @whiskey_disk.configuration = @parameters.merge('repository' => nil) lambda { @whiskey_disk.checkout_main_repository }.should.raise end it 'works from the main repository checkout parent path' do @whiskey_disk.checkout_main_repository @whiskey_disk.buffer.join(' ').should.match(%r{cd /path/to/main[^/]}) end it 'attempts to clone the main repository to the repository checkout path' do @whiskey_disk.checkout_main_repository @whiskey_disk.buffer.join(' ').should.match(%r{clone #{@parameters['repository']} repo}) end it 'makes the main repository clone conditional on the lack of a main repository checkout' do @whiskey_disk.checkout_main_repository @whiskey_disk.buffer.join(' ').should.match(%r{if \[ -e #{@parameters['deploy_to']} \]; then .*; fi}) end it 'does a branch creation checkout of the master branch when no branch is specified' do @whiskey_disk.checkout_main_repository @whiskey_disk.buffer.join(' ').should.match(%r{git checkout -b master origin/master}) end it 'falls back to a regular checkout of the master branch with origin branch when no branch is specified' do @whiskey_disk.checkout_main_repository @whiskey_disk.buffer.join(' ').should.match(%r{\|\| git checkout master origin/master}) end it 'falls back to a regular checkout of the master branch when no branch is specified' do @whiskey_disk.checkout_main_repository @whiskey_disk.buffer.join(' ').should.match(%r{\|\| git checkout master origin/master \|\| git checkout master}) end it 'does a branch creation checkout of the specified branch when a branch is specified' do @whiskey_disk.configuration = @parameters.merge({'branch' => 'production'}) @whiskey_disk.checkout_main_repository @whiskey_disk.buffer.join(' ').should.match(%r{git checkout -b production origin/production}) end it 'falls back to a regular checkout of the specified branch with origin branch when a branch is specified' do @whiskey_disk.configuration = @parameters.merge({'branch' => 'production'}) @whiskey_disk.checkout_main_repository @whiskey_disk.buffer.join(' ').should.match(%r{\|\| git checkout production origin/production}) end it 'falls back to a regular checkout of the specified branch when a branch is specified' do @whiskey_disk.configuration = @parameters.merge({'branch' => 'production'}) @whiskey_disk.checkout_main_repository @whiskey_disk.buffer.join(' ').should.match(%r{\|\| git checkout production origin/production \|\| git checkout production}) end it 'does branch checkouts from the repository path' do @whiskey_disk.checkout_main_repository @whiskey_disk.buffer.join(' ').should.match(%r{cd /path/to/main/repo && git checkout}) end end describe 'checking out the configuration repository' do before do @parameters = { 'deploy_config_to' => '/path/to/config/repo', 'config_repository' => 'git@ogtastic.com:config.git' } @whiskey_disk.configuration = @parameters end it 'fails if the configuration deployment path is not specified' do @whiskey_disk.configuration = @parameters.merge('deploy_config_to' => nil) lambda { @whiskey_disk.checkout_configuration_repository }.should.raise end it 'fails if the configuration repository is not specified' do @whiskey_disk.configuration = @parameters.merge('config_repository' => nil) lambda { @whiskey_disk.checkout_configuration_repository }.should.raise end it 'works from the configuration repository checkout parent path' do @whiskey_disk.checkout_configuration_repository @whiskey_disk.buffer.join(' ').should.match(%r{cd /path/to/config[^/]}) end it 'attempts to clone the configuration repository to the repository checkout path' do @whiskey_disk.checkout_configuration_repository @whiskey_disk.buffer.join(' ').should.match(%r{clone #{@parameters['config_repository']} repo}) end it 'makes the configuration repository clone conditional on the lack of a main repository checkout' do @whiskey_disk.checkout_configuration_repository @whiskey_disk.buffer.join(' ').should.match(%r{if \[ -e #{@parameters['deploy_config_to']} \]; then .*; fi}) end it 'does a branch creation checkout of the master branch when no branch is specified' do @whiskey_disk.checkout_configuration_repository @whiskey_disk.buffer.join(' ').should.match(%r{git checkout -b master origin/master}) end it 'falls back to a regular checkout of the master branch with origin branch when no branch is specified' do @whiskey_disk.checkout_configuration_repository @whiskey_disk.buffer.join(' ').should.match(%r{\|\| git checkout master origin/master}) end it 'falls back to a regular checkout of the master branch when no branch is specified' do @whiskey_disk.checkout_configuration_repository @whiskey_disk.buffer.join(' ').should.match(%r{\|\| git checkout master origin/master \|\| git checkout master}) end it 'does a branch creation checkout of the specified branch when a branch is specified' do @whiskey_disk.configuration = @parameters.merge({'config_branch' => 'production'}) @whiskey_disk.checkout_configuration_repository @whiskey_disk.buffer.join(' ').should.match(%r{git checkout -b production origin/production}) end it 'falls back to a regular checkout of the specified branch with origin branch when a branch is specified' do @whiskey_disk.configuration = @parameters.merge({'config_branch' => 'production'}) @whiskey_disk.checkout_configuration_repository @whiskey_disk.buffer.join(' ').should.match(%r{\|\| git checkout production origin/production}) end it 'falls back to a regular checkout of the specified branch when a branch is specified' do @whiskey_disk.configuration = @parameters.merge({'config_branch' => 'production'}) @whiskey_disk.checkout_configuration_repository @whiskey_disk.buffer.join(' ').should.match(%r{\|\| git checkout production origin/production \|\| git checkout production}) end end describe 'updating the main repository checkout' do before do @parameters = { 'deploy_to' => '/path/to/main/repo' } @whiskey_disk.configuration = @parameters end it 'fails if the deployment path is not specified' do @whiskey_disk.configuration = @parameters.merge('deploy_to' => nil) lambda { @whiskey_disk.update_main_repository_checkout }.should.raise end it 'works from the main repository checkout path' do @whiskey_disk.update_main_repository_checkout @whiskey_disk.buffer.join(' ').should.match(%r{cd /path/to/main/repo}) end it 'clears out any existing git changes data' do @whiskey_disk.update_main_repository_checkout @whiskey_disk.buffer.join(' ').should.match(%r{rm -f /path/to/main/repo/.whiskey_disk_git_changes}) end it 'captures the current git HEAD ref for the current branch' do @whiskey_disk.configuration = @parameters.merge({'branch' => 'production'}) @whiskey_disk.update_main_repository_checkout @whiskey_disk.buffer.join(' ').should.match(%r{ml=\`git log -1 --pretty=format:%H\`}) end it 'captures the current git HEAD ref for the current branch if no branch is specified' do @whiskey_disk.update_main_repository_checkout @whiskey_disk.buffer.join(' ').should.match(%r{ml=\`git log -1 --pretty=format:%H\`}) end it 'attempts to fetch only the master branch from the origin if no branch is specified' do @whiskey_disk.update_main_repository_checkout @whiskey_disk.buffer.join(' ').should.match(%r{git fetch origin \+refs/heads/master:refs/remotes/origin/master}) end it 'attempts to fetch the specified branch from the origin if a branch is specified' do @whiskey_disk.configuration = @parameters.merge({'branch' => 'production'}) @whiskey_disk.update_main_repository_checkout @whiskey_disk.buffer.join(' ').should.match(%r{git fetch origin \+refs/heads/production:refs/remotes/origin/production}) end it 'works from the default branch if no branch is specified' do @whiskey_disk.update_main_repository_checkout @whiskey_disk.buffer.join(' ').should.match(%r{git checkout master}) end it 'works from the specified branch if one is specified' do @whiskey_disk.configuration = @parameters.merge({'branch' => 'production'}) @whiskey_disk.update_main_repository_checkout @whiskey_disk.buffer.join(' ').should.match(%r{git checkout production}) end it 'attempts to reset the master branch from the origin if no branch is specified' do @whiskey_disk.update_main_repository_checkout @whiskey_disk.buffer.join(' ').should.match(%r{git reset --hard origin/master}) end it 'attempts to reset the specified branch from the origin if a branch is specified' do @whiskey_disk.configuration = @parameters.merge({'branch' => 'production'}) @whiskey_disk.update_main_repository_checkout @whiskey_disk.buffer.join(' ').should.match(%r{git reset --hard origin/production}) end it 'collects git changes data' do @whiskey_disk.update_main_repository_checkout @whiskey_disk.buffer.join(' ').should.match(%r{git diff --name-only \$\{ml\}\.\.HEAD > /path/to/main/repo/\.whiskey_disk_git_changes}) end end describe 'updating the configuration repository checkout' do before do @parameters = { 'deploy_config_to' => '/path/to/config/repo', 'deploy_to' => '/path/to/main/repo' } @whiskey_disk.configuration = @parameters end it 'fails if the configuration deployment path is not specified' do @whiskey_disk.configuration = @parameters.merge('deploy_config_to' => nil) lambda { @whiskey_disk.update_configuration_repository_checkout }.should.raise end it 'works from the main repository checkout path' do @whiskey_disk.update_configuration_repository_checkout @whiskey_disk.buffer.join(' ').should.match(%r{cd /path/to/config/repo}) end it 'clears out any existing rsync changes data' do @whiskey_disk.update_configuration_repository_checkout @whiskey_disk.buffer.join(' ').should.match(%r{rm -f /path/to/main/repo/.whiskey_disk_rsync_changes}) end it 'attempts to fetch only the master branch from the origin if no configuration branch is specified' do @whiskey_disk.update_configuration_repository_checkout @whiskey_disk.buffer.join(' ').should.match(%r{git fetch origin \+refs/heads/master:refs/remotes/origin/master}) end it 'attempts to fetch the specified branch from the origin if a configuration branch is specified' do @whiskey_disk.configuration = @parameters.merge({'config_branch' => 'production'}) @whiskey_disk.update_configuration_repository_checkout @whiskey_disk.buffer.join(' ').should.match(%r{git fetch origin \+refs/heads/production:refs/remotes/origin/production}) end it 'attempts to reset the master branch from the origin if no configuration branch is specified' do @whiskey_disk.update_configuration_repository_checkout @whiskey_disk.buffer.join(' ').should.match(%r{git reset --hard origin/master}) end it 'attempts to reset the master branch from the origin if no configuration branch is specified' do @whiskey_disk.configuration = @parameters.merge({'config_branch' => 'production'}) @whiskey_disk.update_configuration_repository_checkout @whiskey_disk.buffer.join(' ').should.match(%r{git reset --hard origin/production}) end end describe 'refreshing the configuration' do before do @parameters = { 'deploy_to' => '/path/to/main/repo', 'deploy_config_to' => '/path/to/config/repo', 'environment' => 'production', 'config_repository' => 'git@git://foo.bar.git', 'config_branch' => 'master', 'config_target' => 'staging', 'project' => 'whiskey_disk' } @whiskey_disk.configuration = @parameters end it 'fails if the main deployment path is not specified' do @whiskey_disk.configuration = @parameters.merge('deploy_to' => nil) lambda { @whiskey_disk.refresh_configuration }.should.raise end it 'fails if the configuration deployment path is not specified' do @whiskey_disk.configuration = @parameters.merge('deploy_config_to' => nil) lambda { @whiskey_disk.refresh_configuration }.should.raise end it 'fails if no project name was specified' do @whiskey_disk.configuration = @parameters.merge('project' => 'unnamed_project') lambda { @whiskey_disk.refresh_configuration }.should.raise end it 'uses rsync to overlay the configuration checkout for the project in the config target onto the main checkout' do @whiskey_disk.refresh_configuration @whiskey_disk.buffer.last.should.match(%r{rsync.* /path/to/config/repo/whiskey_disk/staging/ /path/to/main/repo/}) end it 'captures rsync change data' do @whiskey_disk.refresh_configuration @whiskey_disk.buffer.last.should.match(%r{rsync.* --log-format="%t \[%p\] %i %n".*> /path/to/main/repo/.whiskey_disk_rsync_changes}) end end describe 'running post setup hooks' do before do @whiskey_disk.configuration = { 'deploy_to' => '/path/to/main/repo' } ENV['debug'] = nil end it 'fails if the deployment path is not specified' do @whiskey_disk.configuration = {} lambda { @whiskey_disk.run_post_setup_hooks }.should.raise end it 'works from the main checkout directory' do @whiskey_disk.run_post_setup_hooks @whiskey_disk.buffer.join(' ').should.match(%r{cd /path/to/main/repo}) end describe 'when a post setup script is specified' do describe 'and the script path does not start with a "/"' do before do @whiskey_disk.configuration = { 'deploy_to' => '/path/to/main/repo', 'post_setup_script' => 'path/to/setup/script', 'rake_env' => { 'FOO' => 'BAR' } } end it 'cds to the deploy_to path prior to running the script' do @whiskey_disk.run_post_setup_hooks @whiskey_disk.buffer.join(' ').should.match(%r{cd /path/to/main/repo;.*bash /path/to/main/repo/path/to/setup/script}) end it 'attempts to run the post setup script with the deployment path prepended' do @whiskey_disk.run_post_setup_hooks @whiskey_disk.buffer.join(' ').should.match(%r{bash /path/to/main/repo/path/to/setup/script}) end it 'passes any environment variables when running the post setup script' do @whiskey_disk.run_post_setup_hooks @whiskey_disk.buffer.join(' ').should.match(%r{FOO='BAR' bash /path/to/main/repo/path/to/setup/script}) end it 'enables shell verbosity when debugging is enabled' do ENV['debug'] = 'true' @whiskey_disk.run_post_setup_hooks @whiskey_disk.buffer.join(' ').should.match(%r{bash -x /path/to/main/repo/path/to/setup/script}) end it 'disables shell verbosity when debugging is not enabled' do ENV['debug'] = 'false' @whiskey_disk.run_post_setup_hooks @whiskey_disk.buffer.join(' ').should.match(%r{bash /path/to/main/repo/path/to/setup/script}) end end describe 'and the script path starts with a "/"' do before do @whiskey_disk.configuration = { 'deploy_to' => '/path/to/main/repo', 'post_setup_script' => '/path/to/setup/script', 'rake_env' => { 'FOO' => 'BAR' } } end it 'cds to the deploy_to path prior to running the script' do @whiskey_disk.run_post_setup_hooks @whiskey_disk.buffer.join(' ').should.match(%r{cd /path/to/main/repo;.*bash /path/to/setup/script}) end it 'runs the post setup script using its absolute path' do @whiskey_disk.run_post_setup_hooks @whiskey_disk.buffer.join(' ').should.match(%r{bash /path/to/setup/script}) end it 'passes any environment variables when running the post setup script' do @whiskey_disk.run_post_setup_hooks @whiskey_disk.buffer.join(' ').should.match(%r{FOO='BAR' bash /path/to/setup/script}) end it 'enables shell verbosity when debugging is enabled' do ENV['debug'] = 'true' @whiskey_disk.run_post_setup_hooks @whiskey_disk.buffer.join(' ').should.match(%r{bash -x /path/to/setup/script}) end it 'disables shell verbosity when debugging is not enabled' do ENV['debug'] = 'false' @whiskey_disk.run_post_setup_hooks @whiskey_disk.buffer.join(' ').should.match(%r{bash /path/to/setup/script}) end end end it 'attempts to run the post setup rake tasks' do @whiskey_disk.run_post_setup_hooks @whiskey_disk.buffer.join(' ').should.match(%r{rake.*deploy:post_setup}) end it 'runs the post setup tasks with given rake command' do @parameters = { 'deploy_to' => '/path/to/main/repo', 'rake_command' => 'bundle exec rake' } @whiskey_disk.configuration = @parameters @whiskey_disk.run_post_setup_hooks @whiskey_disk.buffer.join(' ').should.match(%r{bundle exec rake.*deploy:post_setup}) end it 'runs the post setup tasks with default rake command if the rake command is empty' do @parameters = { 'deploy_to' => '/path/to/main/repo', 'rake_command' => '' } @whiskey_disk.configuration = @parameters @whiskey_disk.run_post_setup_hooks @whiskey_disk.buffer.join(' ').should.match(%r{rake.*deploy:post_setup}) end it 'uses the same environment when running the rake tasks' do @whiskey_disk.run_post_setup_hooks @whiskey_disk.buffer.join(' ').should.match(%r{to=#{@env}}) end it 'makes the post setup rake tasks conditional on the presence of a Rakefile in the deployment path' do @whiskey_disk.run_post_setup_hooks @whiskey_disk.buffer.join(' ').should.match(%r{if \[ -e /path/to/main/repo/Rakefile \]; then .*; fi}) end it 'makes the post setup rake tasks conditional on the deploy:post_setup rake task being defined' do @whiskey_disk.run_post_setup_hooks @whiskey_disk.buffer.join(' ').should.match(%r{rakep=\`.*rake -P\` && if \[\[ \`echo "\$\{rakep\}" | grep deploy:post_setup\` != "" \]\];}) end it 'uses the given rake command when checking the existance of the deploy:post_setup task' do @parameters = { 'deploy_to' => '/path/to/main/repo', 'rake_command' => 'bundle exec rake' } @whiskey_disk.configuration = @parameters @whiskey_disk.run_post_setup_hooks @whiskey_disk.buffer.join(' ').should.match(%r{rakep=\`.*bundle exec rake -P\`}) end it 'ensures that any rake ENV variable are set when checking for deploy:post_setup tasks' do @parameters = { 'deploy_to' => '/path/to/main/repo', 'rake_env' => { 'RAILS_ENV' => 'production', 'FOO' => 'bar' } } @whiskey_disk.configuration = @parameters @whiskey_disk.run_post_setup_hooks @parameters['rake_env'].each_pair do |k,v| @whiskey_disk.buffer.join(' ').should.match(%r{#{k}='#{v}' .*rake -P}) end end it 'sets any rake_env variables when running the rake tasks' do @parameters = { 'deploy_to' => '/path/to/main/repo', 'rake_env' => { 'RAILS_ENV' => 'production', 'FOO' => 'bar' } } @whiskey_disk.configuration = @parameters @whiskey_disk.run_post_setup_hooks @parameters['rake_env'].each_pair do |k,v| @whiskey_disk.buffer.join(' ').should.match(%r{#{k}='#{v}' }) end end end describe 'running post deployment hooks' do before do @whiskey_disk.configuration = { 'deploy_to' => '/path/to/main/repo' } ENV['debug'] = nil end it 'fails if the deployment path is not specified' do @whiskey_disk.configuration = {} lambda { @whiskey_disk.run_post_deploy_hooks }.should.raise end it 'works from the main checkout directory' do @whiskey_disk.run_post_deploy_hooks @whiskey_disk.buffer.join(' ').should.match(%r{cd /path/to/main/repo}) end describe 'when a post deployment script is specified' do describe 'and the script path does not start with a "/"' do before do @whiskey_disk.configuration = { 'deploy_to' => '/path/to/main/repo', 'post_deploy_script' => 'path/to/deployment/script', 'rake_env' => { 'FOO' => 'BAR' } } end it 'cds to the deploy_to path prior to running the script' do @whiskey_disk.run_post_deploy_hooks @whiskey_disk.buffer.join(' ').should.match(%r{cd /path/to/main/repo;.*bash /path/to/main/repo/path/to/deployment/script}) end it 'attempts to run the post deployment script with the deployment path prepended' do @whiskey_disk.run_post_deploy_hooks @whiskey_disk.buffer.join(' ').should.match(%r{bash /path/to/main/repo/path/to/deployment/script}) end it 'passes any environment variables when running the post deploy script' do @whiskey_disk.run_post_deploy_hooks @whiskey_disk.buffer.join(' ').should.match(%r{FOO='BAR' bash /path/to/main/repo/path/to/deployment/script}) end it 'enables shell verbosity when debugging is enabled' do ENV['debug'] = 'true' @whiskey_disk.run_post_deploy_hooks @whiskey_disk.buffer.join(' ').should.match(%r{bash -x /path/to/main/repo/path/to/deployment/script}) end it 'disables shell verbosity when debugging is not enabled' do ENV['debug'] = 'false' @whiskey_disk.run_post_deploy_hooks @whiskey_disk.buffer.join(' ').should.match(%r{bash /path/to/main/repo/path/to/deployment/script}) end end describe 'and the script path starts with a "/"' do before do @whiskey_disk.configuration = { 'deploy_to' => '/path/to/main/repo', 'post_deploy_script' => '/path/to/deployment/script', 'rake_env' => { 'FOO' => 'BAR' } } end it 'cds to the deploy_to path prior to running the script' do @whiskey_disk.run_post_deploy_hooks @whiskey_disk.buffer.join(' ').should.match(%r{cd /path/to/main/repo;.*bash /path/to/deployment/script}) end it 'attempts to run the post deployment script using its absolute path' do @whiskey_disk.run_post_deploy_hooks @whiskey_disk.buffer.join(' ').should.match(%r{bash /path/to/deployment/script}) end it 'passes any environment variables when running the post deploy script' do @whiskey_disk.run_post_deploy_hooks @whiskey_disk.buffer.join(' ').should.match(%r{FOO='BAR' bash /path/to/deployment/script}) end it 'enables shell verbosity when debugging is enabled' do ENV['debug'] = 'true' @whiskey_disk.run_post_deploy_hooks @whiskey_disk.buffer.join(' ').should.match(%r{bash -x /path/to/deployment/script}) end it 'disables shell verbosity when debugging is not enabled' do ENV['debug'] = 'false' @whiskey_disk.run_post_deploy_hooks @whiskey_disk.buffer.join(' ').should.match(%r{bash /path/to/deployment/script}) end end end it 'attempts to run the post deployment rake tasks' do @whiskey_disk.run_post_deploy_hooks @whiskey_disk.buffer.join(' ').should.match(%r{rake.*deploy:post_deploy}) end it 'runs the post deployment tasks with given rake command' do @parameters = { 'deploy_to' => '/path/to/main/repo', 'rake_command' => 'bundle exec rake' } @whiskey_disk.configuration = @parameters @whiskey_disk.run_post_deploy_hooks @whiskey_disk.buffer.join(' ').should.match(%r{bundle exec rake.*deploy:post_deploy}) end it 'runs the post deployment tasks with default rake command if the rake command is empty' do @parameters = { 'deploy_to' => '/path/to/main/repo', 'rake_command' => '' } @whiskey_disk.configuration = @parameters @whiskey_disk.run_post_deploy_hooks @whiskey_disk.buffer.join(' ').should.match(%r{rake.*deploy:post_deploy}) end it 'uses the same environment when running the rake tasks' do @whiskey_disk.run_post_deploy_hooks @whiskey_disk.buffer.join(' ').should.match(%r{to=#{@env}}) end it 'makes the post deployment rake tasks conditional on the presence of a Rakefile in the deployment path' do @whiskey_disk.run_post_deploy_hooks @whiskey_disk.buffer.join(' ').should.match(%r{if \[ -e /path/to/main/repo/Rakefile \]; then .*; fi}) end it 'makes the post deployment rake tasks conditional on the deploy:post_deploy rake task being defined' do @whiskey_disk.run_post_deploy_hooks @whiskey_disk.buffer.join(' ').should.match(%r{rakep=\`.*rake -P\` && if \[\[ \`echo "\$\{rakep\}" | grep deploy:post_deploy\` != "" \]\];}) end it 'uses the given rake command when checking the existance of the deploy:post_deploy task' do @parameters = { 'deploy_to' => '/path/to/main/repo', 'rake_command' => 'bundle exec rake' } @whiskey_disk.configuration = @parameters @whiskey_disk.run_post_deploy_hooks @whiskey_disk.buffer.join(' ').should.match(%r{rakep=\`.*bundle exec rake -P\`}) end it 'ensures that any rake ENV variable are set when checking for deploy:post_setup tasks' do @parameters = { 'deploy_to' => '/path/to/main/repo', 'rake_env' => { 'RAILS_ENV' => 'production', 'FOO' => 'bar' } } @whiskey_disk.configuration = @parameters @whiskey_disk.run_post_deploy_hooks @parameters['rake_env'].each_pair do |k,v| @whiskey_disk.buffer.join(' ').should.match(%r{#{k}='#{v}' .*rake -P}) end end it 'sets any rake_env variables when running the rake tasks' do @parameters = { 'deploy_to' => '/path/to/main/repo', 'rake_env' => { 'RAILS_ENV' => 'production', 'FOO' => 'bar' } } @whiskey_disk.configuration = @parameters @whiskey_disk.run_post_deploy_hooks @parameters['rake_env'].each_pair do |k,v| @whiskey_disk.buffer.join(' ').should.match(%r{#{k}='#{v}' }) end end end describe 'bundling up buffered commands for execution' do describe 'when staleness checks are disabled' do it 'returns an empty string if there are no commands' do @whiskey_disk.bundle.should == '' end it 'wraps each command with {} and join with &&s' do @whiskey_disk.enqueue("cd foo/bar/baz || true") @whiskey_disk.enqueue("rsync -avz --progress /yer/mom /yo/") @whiskey_disk.bundle.should == "{ cd foo/bar/baz || true ; } && { rsync -avz --progress /yer/mom /yo/ ; }" end it 'does not wrap the bundled commands inside a staleness check' do @whiskey_disk.enqueue("cd foo/bar/baz || true") @whiskey_disk.enqueue("rsync -avz --progress /yer/mom /yo/") @whiskey_disk.bundle.should == "{ cd foo/bar/baz || true ; } && { rsync -avz --progress /yer/mom /yo/ ; }" end end describe 'when staleness checks are enabled' do before do @whiskey_disk = WhiskeyDisk.new(:staleness_checks => true) end describe 'but not we are not configured for staleness checks on this deployment' do before do ENV['check'] = nil end it 'returns an empty string if there are no commands' do @whiskey_disk.bundle.should == '' end it 'wraps each command with {} and join with &&s' do @whiskey_disk.enqueue("cd foo/bar/baz || true") @whiskey_disk.enqueue("rsync -avz --progress /yer/mom /yo/") @whiskey_disk.bundle.should == "{ cd foo/bar/baz || true ; } && { rsync -avz --progress /yer/mom /yo/ ; }" end it 'does not wrap the bundled commands inside a staleness check' do @whiskey_disk.enqueue("cd foo/bar/baz || true") @whiskey_disk.enqueue("rsync -avz --progress /yer/mom /yo/") @whiskey_disk.bundle.should == "{ cd foo/bar/baz || true ; } && { rsync -avz --progress /yer/mom /yo/ ; }" end end describe 'and we are configured for staleness checks on this deployment' do before do ENV['check'] = 'true' end describe 'when no configuration repository is in use' do before do @deploy_to = '/path/to/main/repo' @repository = 'git@git://foo.bar.git' @parameters = { 'deploy_to' => @deploy_to, 'repository' => @repository } @whiskey_disk.configuration = @parameters end it 'returns an empty string if there are no commands' do @whiskey_disk.bundle.should == '' end it 'wraps each command with {} and join with &&s' do @whiskey_disk.enqueue("cd foo/bar/baz || true") @whiskey_disk.enqueue("rsync -avz --progress /yer/mom /yo/") @whiskey_disk.bundle.should.match(Regexp.new(Regexp.escape("{ cd foo/bar/baz || true ; } && { rsync -avz --progress /yer/mom /yo/ ; }"))) end it 'wraps the bundled commands inside a staleness check which checks only the main repo for staleness' do @whiskey_disk.enqueue("COMMAND") @whiskey_disk.bundle.should.match(Regexp.new(Regexp.escape("if [[ $ml != ${mr%%\t*} ]] ; then { COMMAND ; }"))) end it 'adds a notice message for when the repository is not stale' do @whiskey_disk.enqueue("COMMAND") @whiskey_disk.bundle.should.match(Regexp.new(Regexp.escape("then { COMMAND ; }; else echo \"No changes to deploy.\"; fi"))) end it "queries the head of the main checkout's current branch if no branch is specified" do @whiskey_disk.enqueue("COMMAND") @whiskey_disk.bundle.should.match(Regexp.new(Regexp.escape("cd #{@deploy_to}; ml=\`git log -1 --pretty=format:%H\`;"))) end it "queries the head of the main checkout's current branch if a branch is specified" do @whiskey_disk.configuration = @parameters.merge({'branch' => 'production'}) @whiskey_disk.enqueue("COMMAND") @whiskey_disk.bundle.should.match(Regexp.new(Regexp.escape("cd #{@deploy_to}; ml=\`git log -1 --pretty=format:%H\`;"))) end it "queries the head on the main repository's master branch if no branch is specified" do @whiskey_disk.enqueue("COMMAND") @whiskey_disk.bundle.should.match(Regexp.new(Regexp.escape("mr=\`git ls-remote #{@repository} refs/heads/master\`;"))) end it "queries the head of the main repository's specified branch if a branch is specified" do @whiskey_disk.configuration = @parameters.merge({'branch' => 'production'}) @whiskey_disk.enqueue("COMMAND") @whiskey_disk.bundle.should.match(Regexp.new(Regexp.escape("mr=\`git ls-remote #{@repository} refs/heads/production\`;"))) end it 'does not check a configuration repository for staleness' do @whiskey_disk.enqueue("COMMAND") @whiskey_disk.bundle.should.not.match(/c[lr]=/) end end describe 'when a configuration repository is in use' do before do @deploy_to = '/path/to/main/repo' @repository = 'git@git://foo.bar.git' @deploy_config_to = '/path/to/config/repo' @config_repository = 'git@git://foo.bar-config.git' @parameters = { 'deploy_to' => @deploy_to, 'repository' => @repository, 'deploy_config_to' => @deploy_config_to, 'config_repository' => @config_repository } @whiskey_disk.configuration = @parameters end it 'returns an empty string if there are no commands' do @whiskey_disk.bundle.should == '' end it 'wraps each command with {} and join with &&s' do @whiskey_disk.enqueue("cd foo/bar/baz || true") @whiskey_disk.enqueue("rsync -avz --progress /yer/mom /yo/") @whiskey_disk.bundle.should.match(Regexp.new(Regexp.escape("{ cd foo/bar/baz || true ; } && { rsync -avz --progress /yer/mom /yo/ ; }"))) end it 'wraps the bundled commands inside a staleness check which checks both main and config repos for staleness' do @whiskey_disk.enqueue("COMMAND") @whiskey_disk.bundle.should.match(Regexp.new(Regexp.escape("if [[ $ml != ${mr%%\t*} ]] || [[ $cl != ${cr%%\t*} ]]; then { COMMAND ; }"))) end it 'adds a notice message for when the repositories are not stale' do @whiskey_disk.enqueue("COMMAND") @whiskey_disk.bundle.should.match(Regexp.new(Regexp.escape("then { COMMAND ; }; else echo \"No changes to deploy.\"; fi"))) end it "queries the head of the main checkout's current branch if no branch is specified" do @whiskey_disk.enqueue("COMMAND") @whiskey_disk.bundle.should.match(Regexp.new(Regexp.escape("cd #{@deploy_to}; ml=\`git log -1 --pretty=format:%H\`;"))) end it "queries the head of the main checkout's current branch if a branch is specified" do @whiskey_disk.configuration = @parameters.merge({'branch' => 'production'}) @whiskey_disk.enqueue("COMMAND") @whiskey_disk.bundle.should.match(Regexp.new(Regexp.escape("cd #{@deploy_to}; ml=\`git log -1 --pretty=format:%H\`;"))) end it "queries the head on the main repository's master branch if no branch is specified" do @whiskey_disk.enqueue("COMMAND") @whiskey_disk.bundle.should.match(Regexp.new(Regexp.escape("mr=\`git ls-remote #{@repository} refs/heads/master\`;"))) end it "queries the head of the main repository's specified branch if a branch is specified" do @whiskey_disk.configuration = @parameters.merge({'branch' => 'production'}) @whiskey_disk.enqueue("COMMAND") @whiskey_disk.bundle.should.match(Regexp.new(Regexp.escape("mr=\`git ls-remote #{@repository} refs/heads/production\`;"))) end it "queries the head of the config checkout's current branch if no branch is specified" do @whiskey_disk.enqueue("COMMAND") @whiskey_disk.bundle.should.match(Regexp.new(Regexp.escape("cd #{@deploy_config_to}; cl=\`git log -1 --pretty=format:%H\`;"))) end it "queries the head of the config checkout's current branch if a branch is specified" do @whiskey_disk.configuration = @parameters.merge({'config_branch' => 'production'}) @whiskey_disk.enqueue("COMMAND") @whiskey_disk.bundle.should.match(Regexp.new(Regexp.escape("cd #{@deploy_config_to}; cl=\`git log -1 --pretty=format:%H\`;"))) end it "queries the head on the config repository's master branch if no branch is specified" do @whiskey_disk.enqueue("COMMAND") @whiskey_disk.bundle.should.match(Regexp.new(Regexp.escape("cr=\`git ls-remote #{@config_repository} refs/heads/master\`;"))) end it "queries the head of the config repository's specified branch if a branch is specified" do @whiskey_disk.configuration = @parameters.merge({'config_branch' => 'production'}) @whiskey_disk.enqueue("COMMAND") @whiskey_disk.bundle.should.match(Regexp.new(Regexp.escape("cr=\`git ls-remote #{@config_repository} refs/heads/production\`;"))) end end end end end describe 'determining if a domain is of interest to us' do before do @config = WhiskeyDisk::Config.new @config.stub!(:domain_limit).and_return(false) @whiskey_disk.config = @config end it 'allows specifying a domain' do lambda { @whiskey_disk.domain_of_interest?(:domain) }.should.not.raise(ArgumentError) end it 'requires a domain' do lambda { @whiskey_disk.domain_of_interest? }.should.raise(ArgumentError) end it 'returns true when our configuration does not specify a domain limit' do @config.stub!(:domain_limit).and_return(false) @whiskey_disk.domain_of_interest?('somedomain').should == true end it 'returns true when the specified domain matches the configuration domain limit' do @config.stub!(:domain_limit).and_return('somedomain') @whiskey_disk.domain_of_interest?('somedomain').should == true end it 'returns true when the specified domain matches the configuration domain limit, with a prepended "user@"' do @config.stub!(:domain_limit).and_return('somedomain') @whiskey_disk.domain_of_interest?('user@somedomain').should == true end it 'returns false when the specified domain does not match the configuration domain limit' do @config.stub!(:domain_limit).and_return('otherdomain') @whiskey_disk.domain_of_interest?('somedomain').should == false end end describe 'flushing changes' do before do @cmd = 'ls' @domains = [ { 'name' => 'ogc@ogtastic.com' }, { 'name' => 'foo@example.com' }, { 'name' => 'local' } ] @whiskey_disk.configuration = { 'domain' => @domains } @whiskey_disk.stub!(:domain_of_interest?).and_return(true) @whiskey_disk.stub!(:bundle).and_return(@cmd) @whiskey_disk.stub!(:system) @whiskey_disk.stub!(:puts) end it 'fails if the domain path is not specified' do @whiskey_disk.configuration = {} lambda { @whiskey_disk.flush}.should.raise end it 'uses "run" to issue commands for all remote domains' do @whiskey_disk.should.receive(:run).with({ 'name' => 'ogc@ogtastic.com' }, @cmd) @whiskey_disk.should.receive(:run).with({ 'name' => 'foo@example.com' }, @cmd) @whiskey_disk.flush end it 'uses "shell" to issue commands for any local domains' do @whiskey_disk.should.receive(:shell).with({ 'name' => 'local' }, @cmd) @whiskey_disk.flush end it 'does not issue a command via run for a remote domain which is not of interest' do @whiskey_disk.stub!(:domain_of_interest?).with('ogc@ogtastic.com').and_return(false) @whiskey_disk.should.not.receive(:run).with({ 'name' => 'ogc@ogtastic.com' }, @cmd) @whiskey_disk.flush end it 'does not issue a command via shell for a local domain which is not of interest' do @whiskey_disk.stub!(:domain_of_interest?).with('local').and_return(false) @whiskey_disk.should.not.receive(:shell).with({ 'name' => 'local' }, @cmd) @whiskey_disk.flush end end describe 'when running a command string locally' do before do @domain_name = 'local' @domain = { 'name' => @domain_name } @whiskey_disk.configuration = { 'domain' => [ @domain ] } @whiskey_disk.stub!(:system) @whiskey_disk.stub!(:puts) end it 'accepts a domain and a command string' do lambda { @whiskey_disk.shell(@domain, 'ls') }.should.not.raise(ArgumentError) end it 'requires a domain and a command string' do lambda { @whiskey_disk.shell(@domain) }.should.raise(ArgumentError) end describe 'when debugging is enabled' do before { ENV['debug'] = 'true' } it 'passes the string to the shell with verbosity enabled' do @whiskey_disk.should.receive(:system).with('bash', '-c', "set -x; ls") @whiskey_disk.shell(@domain, 'ls') end it 'includes domain role settings when the domain has roles' do @domain = { 'name' => @domain_name, 'roles' => [ 'web', 'db' ] } @whiskey_disk.configuration = { 'domain' => [ @domain ] } @whiskey_disk.should.receive(:system).with('bash', '-c', "set -x; export WD_ROLES='web:db'; ls") @whiskey_disk.shell(@domain, 'ls') end end describe 'when debugging is not enabled' do before { ENV['debug'] = 'false' } it 'passes the string to the shell without verbosity enabled' do @whiskey_disk.should.receive(:system).with('bash', '-c', "ls") @whiskey_disk.shell(@domain, 'ls') end it 'includes domain role settings when the domain has roles' do @domain = { 'name' => @domain_name, 'roles' => [ 'web', 'db' ] } @whiskey_disk.configuration = { 'domain' => [ @domain ] } @whiskey_disk.should.receive(:system).with('bash', '-c', "export WD_ROLES='web:db'; ls") @whiskey_disk.shell(@domain, 'ls') end end end describe 'when running a command string remotely' do before do @domain_name = 'ogc@ogtastic.com' @domain = { 'name' => @domain_name } @whiskey_disk.configuration = { 'domain' => [ @domain ] } @whiskey_disk.stub!(:system) @whiskey_disk.stub!(:puts) end it 'accepts a domain and a command string' do lambda { @whiskey_disk.run(@domain, 'ls') }.should.not.raise(ArgumentError) end it 'requires a domain and a command string' do lambda { @whiskey_disk.run(@domain) }.should.raise(ArgumentError) end describe "building a command" do it 'includes domain role settings when the domain has roles' do @domain = { 'name' => @domain_name, 'roles' => [ 'web', 'db' ] } @whiskey_disk.configuration = { 'domain' => [ @domain ] } expect(@whiskey_disk.build_command(@domain, 'ls')).to match(/export WD_ROLES='web:db'; ls/) end it 'incldues other arbitrary env vars when specified' do @domain = { 'name' => @domain_name, 'roles' => [ 'web', 'db' ], 'env_vars' => { 'WORKER_COUNT' => 5 } } @whiskey_disk.configuration = { 'domain' => [ @domain ] } cmd = @whiskey_disk.build_command(@domain, 'ls') expect(cmd).to include("export WORKER_COUNT='5';") end end describe 'when debugging is enabled' do before { ENV['debug'] = 'true' } it 'passes the string to ssh for the domain, with verbosity enabled' do @whiskey_disk.should.receive(:system).with('ssh', @domain_name, '-v', "set -x; ls") @whiskey_disk.run(@domain, 'ls') end end describe 'when debugging is not enabled' do before { ENV['debug'] = 'false' } it 'passes the string to ssh for the domain, with verbosity disabled' do @whiskey_disk.should.receive(:system).with('ssh', @domain_name, "ls") @whiskey_disk.run(@domain, 'ls') end end describe 'when ssh_options are specified in the configuration' do before do @domain = { 'name' => @domain_name, 'ssh_options' => [ '-t', '-p 12345' ] } @whiskey_disk.configuration = { 'domain' => [ @domain ] } end it 'includes the ssh options when running ssh' do @whiskey_disk.should.receive(:system).with('ssh', @domain_name, '-t', '-p 12345', 'ls') @whiskey_disk.run(@domain, 'ls') end end end describe 'determining if all the deployments succeeded' do it 'works without arguments' do lambda { @whiskey_disk.success? }.should.not.raise(ArgumentError) end it 'does not allow arguments' do lambda { @whiskey_disk.success?(:foo) }.should.raise(ArgumentError) end it 'returns true if there are no results recorded' do @whiskey_disk.success?.should.be.true end it 'returns true if all recorded results have true statuses' do @whiskey_disk.record_result('1', true) @whiskey_disk.record_result('2', true) @whiskey_disk.record_result('3', true) @whiskey_disk.success?.should.be.true end it 'returns false if any recorded result has a false status' do @whiskey_disk.record_result('1', true) @whiskey_disk.record_result('2', false) @whiskey_disk.record_result('3', true) @whiskey_disk.success?.should.be.false end end describe 'summarizing the results of a run' do before do @whiskey_disk.stub!(:puts) end it 'works without arguments' do lambda { @whiskey_disk.summarize }.should.not.raise(ArgumentError) end it 'does not allow arguments' do lambda { @whiskey_disk.summarize(:foo) }.should.raise(ArgumentError) end it 'outputs a no runs message when no results are recorded' do @whiskey_disk.should.receive(:puts).with('No deployments to report.') @whiskey_disk.summarize end describe 'and there are results recorded' do before do @whiskey_disk.record_result('foo@bar.com', false) @whiskey_disk.record_result('ogc@ogtastic.com', true) @whiskey_disk.record_result('user@example.com', true) end it 'outputs a status line for each recorded deployment run' do @whiskey_disk.should.receive(:puts).with('foo@bar.com => failed.') @whiskey_disk.should.receive(:puts).with('ogc@ogtastic.com => succeeded.') @whiskey_disk.should.receive(:puts).with('user@example.com => succeeded.') @whiskey_disk.summarize end it 'outputs a summary line including the total runs, count of failures and count of successes.' do @whiskey_disk.should.receive(:puts).with('Total: 3 deployments, 2 successes, 1 failure.') @whiskey_disk.summarize end end end end