capistrano-deploy-management 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.DS_Store ADDED
Binary file
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
4
+ .yardoc
5
+ doc
6
+ Gemfile.lock
7
+ vendor/bundle
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --backtrace
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source :rubygems
2
+
3
+ # Specify your gem's dependencies in capistrano-deploy-management.gemspec
4
+ gemspec
5
+
6
+ gem 'rake'
7
+ gem 'rspec'
data/MIT-LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (C) 2012 Sergey Nartimov
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,172 @@
1
+ Capistrano deploy recipes [![TravisCI](https://secure.travis-ci.org/lest/capistrano-deploy-management.png?branch=master)](http://travis-ci.org/lest/capistrano-deploy-management) [![Gemnasium](https://gemnasium.com/lest/capistrano-deploy-management.png)](https://gemnasium.com/lest/capistrano-deploy-management)
2
+ =========================
3
+
4
+ Fast git-based deploy process inspired by https://github.com/blog/470-deployment-script-spring-cleaning.
5
+
6
+ Quickstart with Git and Rails
7
+ -----------------------------
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'capistrano-deploy-management', :group => :development, :require => false
13
+ ```
14
+
15
+ Create a file named `Capfile` in your project root directory:
16
+
17
+ ```ruby
18
+ require 'capistrano-deploy-management'
19
+ use_recipes :git, :bundle, :rails
20
+
21
+ server 'server name or ip address', :web, :app, :db, :primary => true
22
+ set :user, 'user for deploy'
23
+ set :deploy_to, '/deploy/to/path'
24
+ set :repository, 'your git repository'
25
+
26
+ after 'deploy:update', 'bundle:install'
27
+ ```
28
+
29
+ And then execute:
30
+
31
+ bundle
32
+
33
+ To setup:
34
+
35
+ cap deploy:setup
36
+
37
+ Then when you push some changes to git repository simply run:
38
+
39
+ cap deploy
40
+
41
+ Or if you have migrations (`:rails` recipe should be used in Capfile: `use_recipe :rails`):
42
+
43
+ cap deploy:migrations
44
+
45
+ To look through the changes to be deployed:
46
+
47
+ cap deploy:pending
48
+
49
+ If you want to update to a specific commit (e.g. to rollback):
50
+
51
+ cap deploy COMMIT=foobarbaz
52
+
53
+ Note: it may be required to run `bundle exec cap ...` instead of `cap ...`.
54
+
55
+ Multistage
56
+ ----------
57
+
58
+ Basic usage:
59
+
60
+ ```ruby
61
+ use_recipe :multistage
62
+
63
+ set :default_stage, :development
64
+
65
+ stage :development do
66
+ ...
67
+ end
68
+
69
+ stage :production do
70
+ ...
71
+ end
72
+ ```
73
+
74
+ You can also pass options that allow setting variables and default stage:
75
+
76
+ ```ruby
77
+ stage :development, :branch => :develop, :default => true
78
+ stage :production, :branch => :master
79
+ ```
80
+
81
+ When branches are specified for stages and git recipe is used
82
+ it will automatically select stage based on current local branch.
83
+
84
+ Bundle
85
+ ------
86
+
87
+ Use recipe:
88
+
89
+ ```ruby
90
+ use_recipe :bundle
91
+ ```
92
+
93
+ And add callback to run `bundle install` on each deploy:
94
+
95
+ ```ruby
96
+ after 'deploy:update', 'bundle:install'
97
+ ```
98
+
99
+ Rails Assets
100
+ ------------
101
+
102
+ Use recipe:
103
+
104
+ ```ruby
105
+ use_recipe :rails_assets
106
+ ```
107
+
108
+ Add callback to precompile assets after update:
109
+
110
+ ```ruby
111
+ after 'deploy:update', 'deploy:assets:precompile'
112
+ ```
113
+
114
+ Passenger
115
+ ---------
116
+
117
+ Use recipe:
118
+
119
+ ```ruby
120
+ use_recipe :passenger
121
+ ```
122
+
123
+ It will automatically do `touch tmp/restart.txt` on each deploy.
124
+
125
+ Unicorn
126
+ -------
127
+
128
+ Use recipe:
129
+
130
+ ```ruby
131
+ use_recipe :unicorn
132
+ ```
133
+
134
+ You can setup callback to reload unicorn after deploy is done:
135
+
136
+ ```ruby
137
+ after 'deploy:restart', 'unicorn:reload'
138
+ ```
139
+
140
+ Puma
141
+ -------
142
+
143
+ Use recipe:
144
+
145
+ ```ruby
146
+ use_recipe :puma
147
+ ```
148
+
149
+ You can setup callback to reload puma after deploy is done:
150
+
151
+ ```ruby
152
+ after 'deploy:restart', 'puma:reload'
153
+ ```
154
+
155
+ Whenever
156
+ --------
157
+
158
+ Use recipe:
159
+
160
+ ```ruby
161
+ use_recipe :whenever
162
+ ```
163
+
164
+ To automatically update crontab file:
165
+
166
+ ```ruby
167
+ after 'deploy:restart', 'whenever:update_crontab'
168
+ ```
169
+
170
+ You can also clear crontab file with command:
171
+
172
+ cap whenever:clear_crontab
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+
6
+ desc 'Run all specs in spec directory'
7
+ RSpec::Core::RakeTask.new
8
+
9
+ task :default => :spec
@@ -0,0 +1,16 @@
1
+ # -*- encoding: utf-8 -*-
2
+ Gem::Specification.new do |s|
3
+ s.name = 'capistrano-deploy-management'
4
+ s.version = '0.1.0'
5
+ s.platform = Gem::Platform::RUBY
6
+ s.authors = ['Dominik Rodler']
7
+ s.email = ['dominik.rodler@gmail.com']
8
+ s.homepage = 'https://github.com/drdla/capistrano-deploy-management'
9
+ s.summary = 'Capistrano deploy recipes'
10
+
11
+ s.files = `git ls-files`.split("\n")
12
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
13
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
14
+
15
+ s.add_dependency('capistrano', '~> 2.9')
16
+ end
data/lib/.DS_Store ADDED
Binary file
@@ -0,0 +1,57 @@
1
+ require 'capistrano'
2
+
3
+ module CapistranoDeployManagement
4
+ def self.load_into(configuration)
5
+ configuration.load do
6
+ @used_recipes = []
7
+
8
+ class << self
9
+ attr_reader :used_recipes
10
+ end
11
+
12
+ def use_recipe(recipe_name)
13
+ return if @used_recipes.include?(recipe_name.to_sym)
14
+
15
+ begin
16
+ require "capistrano-deploy-management/#{recipe_name}"
17
+
18
+ recipe = CapistranoDeployManagement.const_get(recipe_name.to_s.capitalize.gsub(/_(\w)/) { $1.upcase })
19
+ recipe.load_into(self)
20
+ @used_recipes << recipe.to_s.split('::').last.downcase.to_sym
21
+ rescue LoadError
22
+ abort "Did you misspell `#{recipe_name}` recipe name?"
23
+ end
24
+ end
25
+
26
+ def use_recipes(*recipes)
27
+ recipes.each do |recipe|
28
+ use_recipe(recipe)
29
+ end
30
+ end
31
+
32
+ def using_recipe?(recipe)
33
+ used_recipes.include?(recipe.to_sym)
34
+ end
35
+
36
+ namespace :deploy do
37
+ desc 'Run deploy'
38
+ task :default do
39
+ update
40
+ restart
41
+ end
42
+
43
+ task :update do
44
+ # nothing
45
+ end
46
+
47
+ task :restart do
48
+ # nothing
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ if Capistrano::Configuration.instance
56
+ CapistranoDeployManagement.load_into(Capistrano::Configuration.instance)
57
+ end
@@ -0,0 +1,21 @@
1
+ module CapistranoDeployManagement
2
+ module Bundle
3
+ def self.load_into(configuration)
4
+ configuration.load do
5
+ namespace :bundle do
6
+ desc 'Install gems'
7
+ task :install, :except => {:no_release => true} do
8
+ bundle_cmd = fetch(:bundle_cmd, 'bundle')
9
+ bundle_flags = fetch(:bundle_flags, '--deployment --quiet')
10
+ bundle_without = [*fetch(:bundle_without, [:development, :test])].compact
11
+
12
+ args = [bundle_flags.to_s]
13
+ args << "--without #{bundle_without.join(' ')}" unless bundle_without.empty?
14
+
15
+ run "cd #{current_path} && #{bundle_cmd} install #{args.join(' ')}"
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,2 @@
1
+ require 'capistrano-deploy-management/bundle'
2
+ CapistranoDeployManagement::Bundler = CapistranoDeployManagement::Bundle
@@ -0,0 +1,42 @@
1
+ module CapistranoDeployManagement
2
+ module Git
3
+ def self.load_into(configuration)
4
+ configuration.load do
5
+ ssh_options[:forward_agent] = true
6
+
7
+ set(:application) { repository.slice(/[^\/:]+?(?=\.git$)/) }
8
+ set(:repository) { abort "Please specify repository, set :repository, 'foo'" }
9
+ set :branch, 'master'
10
+ set :enable_submodules, false
11
+
12
+ set(:current_revision) { capture("cd #{current_path} && git rev-parse HEAD").chomp }
13
+
14
+ set :local_branch do
15
+ `git symbolic-ref HEAD 2> /dev/null`.strip.sub('refs/heads/', '')
16
+ end
17
+
18
+ namespace :deploy do
19
+ desc 'Setup'
20
+ task :setup, :except => {:no_release => true} do
21
+ run "mkdir -p `dirname #{current_path}` && git clone --no-checkout #{repository} #{deploy_to}"
22
+ update
23
+ end
24
+
25
+ desc 'Update the deployed code'
26
+ task :update, :except => {:no_release => true} do
27
+ commit = ENV['COMMIT'] || "origin/#{branch}"
28
+ command = ["cd #{current_path}", 'git fetch origin', "git reset --hard #{commit}"]
29
+ command += ['git submodule init', 'git submodule -q sync', 'git submodule -q update'] if enable_submodules
30
+ run command.join(' && ')
31
+ end
32
+
33
+ desc 'Show pending commits'
34
+ task :pending do
35
+ commit = ENV['COMMIT'] || "origin/#{branch}"
36
+ system("git log --pretty=medium --stat #{current_revision}..#{commit}")
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,28 @@
1
+ module CapistranoDeployManagement
2
+ module Memcached
3
+ def self.load_into(configuration)
4
+ configuration.load do
5
+ namespace :deploy do
6
+ namespace :memcached do
7
+ desc "Restart the Memcache daemon."
8
+ task :restart, :roles => :app do
9
+ deploy.memcached.stop
10
+ deploy.memcached.start
11
+ end
12
+
13
+ desc "Start the Memcache daemon."
14
+ task :start, :roles => :app do
15
+ invoke_command "memcached -P #{current_path}/shared/log/memcached.pid -d", :via => run_method
16
+ end
17
+
18
+ desc "Stop the Memcache daemon."
19
+ task :stop, :roles => :app do
20
+ pid_file = "#{current_path}/shared/log/memcached.pid"
21
+ invoke_command("killall -9 memcached", :via => run_method) if File.exist?(pid_file)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,77 @@
1
+ module CapistranoDeployManagement
2
+ module Multistage
3
+ def self.load_into(configuration)
4
+ configuration.load do
5
+ set :multistage_config, Config.new(configuration)
6
+
7
+ def define_stage(*args, &block)
8
+ warn "[DEPRECATION] `define_stage` is deprecated, use `stage` instead"
9
+ stage(*args, &block)
10
+ end
11
+
12
+ def stage(name, options={}, &block)
13
+ set :default_stage, name.to_sym if options.delete(:default)
14
+ multistage_config.stage(name, options)
15
+ callbacks[:start].detect { |c| c.source == 'multistage:ensure' }.except << name.to_s
16
+
17
+ task(name) do
18
+ set :current_stage, name.to_s
19
+ options.each do |k, v|
20
+ set k, v if v
21
+ end
22
+ block.call if block
23
+ end
24
+ end
25
+
26
+ namespace :multistage do
27
+ task :ensure do
28
+ unless exists?(:current_stage)
29
+ if stage = multistage_config.inferred_stage
30
+ find_and_execute_task(stage.name)
31
+ elsif exists?(:default_stage)
32
+ find_and_execute_task(default_stage)
33
+ else
34
+ stage_names = multistage_config.stages.map(&:name)
35
+ abort "No stage specified. Please specify one of: #{stage_names.join(', ')} (e.g. `cap #{stage_names.first} #{ARGV.last}')"
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ on :start, 'multistage:ensure'
42
+ end
43
+ end
44
+
45
+ # Handles multistage configuration
46
+ class Config
47
+ attr_reader :config
48
+ attr_reader :stages
49
+
50
+ def initialize(config)
51
+ @config = config
52
+ @stages = []
53
+ end
54
+
55
+ def stage(name, options={})
56
+ stages << Stage.new(name, options)
57
+ end
58
+
59
+ def inferred_stage
60
+ if config.using_recipe?(:git)
61
+ branch = config.local_branch
62
+ stages.find { |stage| stage.options[:branch].to_s == branch }
63
+ end
64
+ end
65
+ end
66
+
67
+ class Stage
68
+ attr_reader :name
69
+ attr_reader :options
70
+
71
+ def initialize(name, options={})
72
+ @name = name
73
+ @options = options
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,16 @@
1
+ module CapistranoDeployManagement
2
+ module Passenger
3
+ def self.load_into(configuration)
4
+ configuration.load do
5
+ namespace :passenger do
6
+ desc 'Restart passenger'
7
+ task :restart, :roles => :app, :except => {:no_release => true} do
8
+ run "touch #{current_path}/tmp/restart.txt"
9
+ end
10
+ end
11
+
12
+ after 'deploy:restart', 'passenger:restart'
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,31 @@
1
+ module CapistranoDeployManagement
2
+ module Rails
3
+ def self.load_into(configuration)
4
+ configuration.load do
5
+ set :rake do
6
+ if using_recipe?(:bundle)
7
+ 'bundle exec rake'
8
+ else
9
+ 'rake'
10
+ end
11
+ end
12
+
13
+ set(:rails_env) { 'production' }
14
+
15
+ namespace :deploy do
16
+ desc 'Deploy & migrate'
17
+ task :migrations do
18
+ update
19
+ migrate
20
+ restart
21
+ end
22
+
23
+ desc 'Run migrations'
24
+ task :migrate, :roles => :db, :only => {:primary => true} do
25
+ run "cd #{current_path} && RAILS_ENV=#{rails_env} #{rake} db:migrate"
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,28 @@
1
+ module CapistranoDeployManagement
2
+ module RailsAssets
3
+ def self.load_into(configuration)
4
+ configuration.load do
5
+ use_recipe :rails
6
+
7
+ namespace :deploy do
8
+ namespace :assets do
9
+ desc 'Precompile assets.'
10
+ task :precompile do
11
+ run "cd #{current_path} && RAILS_ENV=#{rails_env} RAILS_GROUPS=assets #{rake} assets:precompile"
12
+ end
13
+
14
+ desc 'Clean assets.'
15
+ task :clean do
16
+ run "cd #{current_path} && RAILS_ENV=#{rails_env} RAILS_GROUPS=assets #{rake} assets:clean"
17
+ end
18
+
19
+ desc 'Clear application cache (e.g. Memcached).'
20
+ task :refresh_cache, roles: :app do
21
+ run "cd #{current_path} && rake cache:clear RAILS_ENV=#{rails_env}"
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,27 @@
1
+ module CapistranoDeployManagement
2
+ module Rvm
3
+ def self.load_into(configuration)
4
+ configuration.load do
5
+ set :rvm_ruby_string, 'default'
6
+ set :rvm_path, '/usr/local/rvm'
7
+
8
+ set(:rvm_shell_path) { "#{rvm_path}/bin/rvm-shell" }
9
+
10
+ set :default_shell do
11
+ shell = rvm_shell_path
12
+ ruby = rvm_ruby_string.to_s.strip
13
+ shell = "rvm_path=#{rvm_path} #{shell} '#{ruby}'" unless ruby.empty?
14
+
15
+ shell
16
+ end
17
+
18
+ if File.exists?('.rvmrc')
19
+ matches = File.read('.rvmrc').scan(/^rvm\s+use\s+.*?([\w\-\.]+@[\w\-]+).*$/)
20
+ if matches.any?
21
+ set :rvm_ruby_string, matches.last.first
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ module CapistranoDeployManagement
2
+ module Unicorn
3
+ def self.load_into(configuration)
4
+ configuration.load do
5
+ set(:unicorn_config) { "#{deploy_to}/current/config/unicorn.rb" }
6
+ set(:unicorn_pidfile) { "#{deploy_to}/shared/pids/unicorn.pid" }
7
+
8
+ namespace :unicorn do
9
+ desc 'Reload unicorn'
10
+ task :reload, :roles => :app, :except => {:no_release => true} do
11
+ run "test -s #{unicorn_pidfile} && kill -s USR2 `cat #{unicorn_pidfile}` || echo 'pidfile does not exist'"
12
+ end
13
+
14
+ desc 'Stop unicorn'
15
+ task :stop, :roles => :app, :except => {:no_release => true} do
16
+ run "test -s #{unicorn_pidfile} && kill -s QUIT `cat #{unicorn_pidfile}` || echo 'pidfile does not exist'"
17
+ end
18
+
19
+ desc 'Reexecute unicorn'
20
+ task :reexec, :roles => :app, :except => {:no_release => true} do
21
+ run "test ! -s #{unicorn_pidfile} && unicorn -c #{unicorn_config} -E production -D || echo 'pidfile already exists'"
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,35 @@
1
+ module CapistranoDeployManagement
2
+ module Whenever
3
+ def self.load_into(configuration)
4
+ configuration.load do
5
+ set :whenever_cmd do
6
+ if using_recipe?(:bundle)
7
+ 'bundle exec whenever'
8
+ else
9
+ 'whenever'
10
+ end
11
+ end
12
+
13
+ set :whenever_identifier do
14
+ if using_recipe?(:multistage)
15
+ "#{application}_#{current_stage}"
16
+ else
17
+ application
18
+ end
19
+ end
20
+
21
+ namespace :whenever do
22
+ desc 'Update crontab file'
23
+ task :update_crontab, :roles => :db, :only => {:primary => true} do
24
+ run "cd #{current_path} && #{whenever_cmd} --update-crontab #{whenever_identifier}"
25
+ end
26
+
27
+ desc 'Clear crontab file'
28
+ task :clear_crontab, :roles => :db, :only => {:primary => true} do
29
+ run "cd #{current_path} && #{whenever_cmd} --clear-crontab #{whenever_identifier}"
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
data/spec/.DS_Store ADDED
Binary file
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'bundler' do
4
+ before do
5
+ mock_config do
6
+ use_recipes :bundler
7
+ set :deploy_to, '/foo/bar'
8
+ end
9
+ end
10
+
11
+ it 'uses bundler alias for bundle recipe' do
12
+ config.used_recipes.should == [:bundle]
13
+ end
14
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'deploy' do
4
+ before do
5
+ mock_config do
6
+ use_recipes :git, :rails
7
+ set :deploy_to, '/foo/bar'
8
+ end
9
+ end
10
+
11
+ it 'returns used recipes' do
12
+ config.used_recipes.should == [:git, :rails]
13
+ end
14
+
15
+ it 'checks if recipe is used' do
16
+ config.should be_using_recipe(:git)
17
+ config.should_not be_using_recipe(:bundle)
18
+ end
19
+
20
+ it 'uses recipe once' do
21
+ config.use_recipe :git
22
+ config.used_recipes.should == [:git, :rails]
23
+ end
24
+
25
+ it 'aborts when recipe name misspelled' do
26
+ with_stderr do |output|
27
+ expect { config.use_recipe(:rvn) }.to raise_error(SystemExit)
28
+ output.should include('Are you misspelled `rvn` recipe name?')
29
+ end
30
+ end
31
+
32
+ describe 'deploy' do
33
+ it 'runs update and restart' do
34
+ cli_execute 'deploy'
35
+ config.should have_executed('deploy:update', 'deploy:restart')
36
+ end
37
+ end
38
+ end
data/spec/git_spec.rb ADDED
@@ -0,0 +1,77 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'git' do
4
+ before do
5
+ mock_config do
6
+ use_recipe :git
7
+ set :deploy_to, '/foo/bar'
8
+ end
9
+
10
+ ENV.delete('COMMIT')
11
+ end
12
+
13
+ it 'has branch' do
14
+ config.branch.should == 'master'
15
+ end
16
+
17
+ context 'with repository' do
18
+ before do
19
+ mock_config { set :repository, 'git@example.com/test-app.git' }
20
+ end
21
+
22
+ it 'sets application from repo' do
23
+ config.application.should == 'test-app'
24
+ end
25
+
26
+ describe 'deploy:setup' do
27
+ it 'clones repository' do
28
+ cli_execute 'deploy:setup'
29
+ config.should have_run('mkdir -p `dirname /foo/bar` && git clone --no-checkout git@example.com/test-app.git /foo/bar')
30
+ end
31
+
32
+ it 'invokes update during setup' do
33
+ config.namespaces[:deploy].should_receive(:update)
34
+ cli_execute 'deploy:setup'
35
+ end
36
+ end
37
+
38
+ describe 'deploy:update' do
39
+ it 'updates' do
40
+ cli_execute 'deploy:update'
41
+ config.should have_run('cd /foo/bar && git fetch origin && git reset --hard origin/master')
42
+ end
43
+
44
+ it 'updates submodules' do
45
+ mock_config { set :enable_submodules, true }
46
+ cli_execute 'deploy:update'
47
+ config.should have_run('cd /foo/bar && git fetch origin && git reset --hard origin/master && git submodule init && git submodule -q sync && git submodule -q update')
48
+ end
49
+
50
+ it 'updates to specific commit' do
51
+ cli_execute 'deploy:update', 'COMMIT=foobarbaz'
52
+ config.should have_run('cd /foo/bar && git fetch origin && git reset --hard foobarbaz')
53
+ end
54
+ end
55
+ end
56
+
57
+ it 'has current revision' do
58
+ config.should_receive(:capture).with('cd /foo/bar && git rev-parse HEAD') { "baz\n" }
59
+ config.current_revision.should == 'baz'
60
+ end
61
+
62
+ it 'shows pending' do
63
+ config.should_receive(:current_revision) { 'baz' }
64
+ config.namespaces[:deploy].should_receive(:system).with('git log --pretty=medium --stat baz..origin/master')
65
+ cli_execute 'deploy:pending'
66
+ end
67
+
68
+ it 'shows pending against specific commit' do
69
+ config.should_receive(:current_revision) { 'baz' }
70
+ config.namespaces[:deploy].should_receive(:system).with('git log --pretty=medium --stat baz..foobarbaz')
71
+ cli_execute 'deploy:pending', 'COMMIT=foobarbaz'
72
+ end
73
+
74
+ it 'sets forward agent' do
75
+ config.ssh_options[:forward_agent].should == true
76
+ end
77
+ end
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'multistage' do
4
+ before do
5
+ mock_config do
6
+ use_recipes :multistage
7
+
8
+ set :default_stage, :development
9
+ stage(:development, :branch => 'develop') { set :foo, 'bar' }
10
+ stage(:production, :branch => 'master') { set :foo, 'baz' }
11
+ stage :another_stage, :foo => 'bar'
12
+
13
+ task(:example) {}
14
+ end
15
+ end
16
+
17
+ it 'uses default stage' do
18
+ cli_execute 'example'
19
+ config.current_stage.should == 'development'
20
+ config.foo.should == 'bar'
21
+ end
22
+
23
+ it 'aborts when no stage selected' do
24
+ with_stderr do |output|
25
+ config.unset :default_stage
26
+ expect { cli_execute 'example' }.to raise_error(SystemExit)
27
+ output.should include('No stage specified. Please specify one of: development, production')
28
+ end
29
+ end
30
+
31
+ it 'uses specified stage' do
32
+ cli_execute %w[production example]
33
+ config.current_stage.should == 'production'
34
+ config.foo.should == 'baz'
35
+ end
36
+
37
+ it 'sets variables from options' do
38
+ cli_execute 'another_stage'
39
+ config.foo.should == 'bar'
40
+ end
41
+
42
+ it 'accepts default option' do
43
+ mock_config { stage :to_be_default, :default => true }
44
+ config.default_stage.should == :to_be_default
45
+ end
46
+
47
+ context 'with git' do
48
+ before do
49
+ mock_config { use_recipe :git }
50
+ end
51
+
52
+ it 'infers stage using local branch' do
53
+ config.stub(:local_branch) { 'master' }
54
+ cli_execute 'example'
55
+ config.current_stage.should == 'production'
56
+ config.branch.should == 'master'
57
+ end
58
+
59
+ it 'uses default state when local branch not matches' do
60
+ config.stub(:local_branch) { 'foo' }
61
+ cli_execute 'example'
62
+ config.current_stage.should == 'development'
63
+ config.branch.should == 'develop'
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'passenger' do
4
+ before do
5
+ mock_config do
6
+ use_recipe :passenger
7
+ set :deploy_to, '/foo/bar'
8
+ end
9
+ end
10
+
11
+ describe 'passenger:restart' do
12
+ it 'touches tmp/restart.txt' do
13
+ cli_execute 'passenger:restart'
14
+ config.should have_run('touch /foo/bar/tmp/restart.txt')
15
+ end
16
+
17
+ it 'is executed after deploy:restart' do
18
+ cli_execute 'deploy:restart'
19
+ config.should have_executed('deploy:restart', 'passenger:restart')
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'rails assets' do
4
+ before do
5
+ mock_config do
6
+ use_recipe :rails_assets
7
+ set :deploy_to, '/foo/bar'
8
+ end
9
+ end
10
+
11
+ it 'uses rails recipe' do
12
+ config.should be_using_recipe(:rails)
13
+ end
14
+
15
+ describe 'deploy:assets:precompile' do
16
+ it 'runs precompile' do
17
+ cli_execute 'deploy:assets:precompile'
18
+ config.should have_run('cd /foo/bar && RAILS_ENV=production RAILS_GROUPS=assets rake assets:precompile')
19
+ end
20
+
21
+ it 'uses bundle command' do
22
+ mock_config { use_recipe :bundle }
23
+ cli_execute 'deploy:assets:precompile'
24
+ config.should have_run('cd /foo/bar && RAILS_ENV=production RAILS_GROUPS=assets bundle exec rake assets:precompile')
25
+ end
26
+ end
27
+
28
+ describe 'deploy:assets:clean' do
29
+ it 'runs clean' do
30
+ cli_execute 'deploy:assets:clean'
31
+ config.should have_run('cd /foo/bar && RAILS_ENV=production RAILS_GROUPS=assets rake assets:clean')
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'rails' do
4
+ before do
5
+ mock_config do
6
+ use_recipe :rails
7
+ set :deploy_to, '/foo/bar'
8
+ end
9
+ end
10
+
11
+ describe 'deploy:migrate' do
12
+ it 'runs rake db:migrate' do
13
+ cli_execute 'deploy:migrate'
14
+ config.should have_run('cd /foo/bar && RAILS_ENV=production rake db:migrate')
15
+ end
16
+
17
+ it 'runs bundle exec db:migrate when using with bundle' do
18
+ mock_config { use_recipe :bundle }
19
+ cli_execute 'deploy:migrate'
20
+ config.should have_run('cd /foo/bar && RAILS_ENV=production bundle exec rake db:migrate')
21
+ end
22
+ end
23
+
24
+ describe 'deploy:migrations' do
25
+ it 'runs update, migrate and restart' do
26
+ cli_execute 'deploy:migrations'
27
+ config.should have_executed('deploy:update', 'deploy:migrate', 'deploy:restart')
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,102 @@
1
+ require 'capistrano-deploy-management'
2
+ require 'capistrano/cli'
3
+
4
+ module CapistranoDeployManagement
5
+ module Spec
6
+ module ConfigurationExtension
7
+ def run(cmd, options={}, &block)
8
+ runned_commands[cmd] = {:options => options, :block => block}
9
+ end
10
+
11
+ def execute_task(task)
12
+ executed_tasks << task.fully_qualified_name
13
+ super
14
+ end
15
+
16
+ def runned_commands
17
+ @runned_commands ||= {}
18
+ end
19
+
20
+ def executed_tasks
21
+ @executed_tasks ||= []
22
+ end
23
+ end
24
+
25
+ module Helper
26
+ def mock_config(new_instance = false, &block)
27
+ if !@config || new_instance
28
+ @config = Capistrano::Configuration.new(:output => StringIO.new)
29
+ @config.extend(CapistranoDeployManagement::Spec::ConfigurationExtension)
30
+ CapistranoDeployManagement.load_into(@config)
31
+ end
32
+
33
+ @config.instance_eval(&block)
34
+ end
35
+
36
+ def config
37
+ @config
38
+ end
39
+
40
+ def cli_execute(*args)
41
+ config = @config
42
+ cli = Capistrano::CLI.parse(args.flatten)
43
+ cli.instance_eval do
44
+ (class << self; self end).send(:define_method, :instantiate_configuration) do |options|
45
+ config
46
+ end
47
+ end
48
+
49
+ cli.execute!
50
+ end
51
+
52
+ def with_stderr
53
+ original, $stderr = $stderr, StringIO.new
54
+ output = Object.new
55
+ class << output
56
+ instance_methods.each { |m| undef_method m unless m =~ /^__|^object_id$|^instance_eval$/ }
57
+ end
58
+ def output.method_missing(*args, &block)
59
+ ($stderr.rewind && $stderr.read).__send__(*args, &block)
60
+ end
61
+
62
+ yield output
63
+ ensure
64
+ $stderr = original
65
+ end
66
+ end
67
+
68
+ module Matchers
69
+ extend RSpec::Matchers::DSL
70
+
71
+ define :have_run do |cmd|
72
+ match do |configuration|
73
+ configuration.runned_commands[cmd]
74
+ end
75
+
76
+ failure_message_for_should do |configuration|
77
+ "expected configuration to run #{cmd}, but it wasn't found in #{configuration.runned_commands.keys}"
78
+ end
79
+ end
80
+
81
+ define :have_executed do |*tasks|
82
+ match do |configuration|
83
+ expected = tasks.dup
84
+ configuration.executed_tasks.each do |actual|
85
+ expected.shift if actual == expected.first
86
+ end
87
+
88
+ expected.empty?
89
+ end
90
+
91
+ failure_message_for_should do |configuration|
92
+ "expected configuration to execute #{tasks}, but it executed #{configuration.executed_tasks.keys}"
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ RSpec.configure do |config|
100
+ config.include CapistranoDeployManagement::Spec::Helper
101
+ config.include CapistranoDeployManagement::Spec::Matchers
102
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'unicorn' do
4
+ before do
5
+ mock_config { use_recipes :unicorn }
6
+ end
7
+
8
+ it 'has default unicorn pid' do
9
+ mock_config { set :deploy_to, '/foo/bar' }
10
+ config.unicorn_pid.should == '`cat /foo/bar/tmp/pids/unicorn.pid`'
11
+ end
12
+
13
+ context 'signals' do
14
+ before do
15
+ mock_config { set :unicorn_pid, '/foo.pid' }
16
+ end
17
+
18
+ it 'sends HUP' do
19
+ cli_execute 'unicorn:reload'
20
+ config.should have_run('kill -HUP /foo.pid')
21
+ end
22
+
23
+ it 'sends QUIT' do
24
+ cli_execute 'unicorn:stop'
25
+ config.should have_run('kill -QUIT /foo.pid')
26
+ end
27
+
28
+ it 'sends USR2' do
29
+ cli_execute 'unicorn:reexec'
30
+ config.should have_run('kill -USR2 /foo.pid')
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'whenever' do
4
+ before do
5
+ mock_config { use_recipe :whenever}
6
+ end
7
+
8
+ describe 'whenever_identifier' do
9
+ before do
10
+ mock_config { set :application, 'foo' }
11
+ end
12
+
13
+ it 'defaults to application' do
14
+ config.whenever_identifier.should == 'foo'
15
+ end
16
+
17
+ it 'default to application with stage when using multistage' do
18
+ mock_config do
19
+ use_recipe :multistage
20
+ set :current_stage, 'bar'
21
+ end
22
+ config.whenever_identifier.should == 'foo_bar'
23
+ end
24
+ end
25
+
26
+ describe 'whenever_cmd' do
27
+ it 'has default value' do
28
+ config.whenever_cmd.should == 'whenever'
29
+ end
30
+
31
+ it 'respects bundle recipe' do
32
+ mock_config { use_recipe :bundle }
33
+ config.whenever_cmd.should == 'bundle exec whenever'
34
+ end
35
+ end
36
+
37
+ describe 'whenever:update_crontab' do
38
+ it 'runs command' do
39
+ mock_config do
40
+ set :deploy_to, '/foo/bar'
41
+ set :whenever_cmd, 'wc'
42
+ set :whenever_identifier, 'wi'
43
+ end
44
+ cli_execute 'whenever:update_crontab'
45
+ config.should have_run('cd /foo/bar && wc --update-crontab wi')
46
+ end
47
+ end
48
+ end
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: capistrano-deploy-management
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Dominik Rodler
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-08-04 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: capistrano
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '2.9'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '2.9'
30
+ description:
31
+ email:
32
+ - dominik.rodler@gmail.com
33
+ executables: []
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - .DS_Store
38
+ - .gitignore
39
+ - .rspec
40
+ - .travis.yml
41
+ - Gemfile
42
+ - MIT-LICENSE
43
+ - README.md
44
+ - Rakefile
45
+ - capistrano-deploy-management.gemspec
46
+ - lib/.DS_Store
47
+ - lib/capistrano-deploy-management.rb
48
+ - lib/capistrano-deploy-management/bundle.rb
49
+ - lib/capistrano-deploy-management/bundler.rb
50
+ - lib/capistrano-deploy-management/git.rb
51
+ - lib/capistrano-deploy-management/memcached.rb
52
+ - lib/capistrano-deploy-management/multistage.rb
53
+ - lib/capistrano-deploy-management/passenger.rb
54
+ - lib/capistrano-deploy-management/rails.rb
55
+ - lib/capistrano-deploy-management/rails_assets.rb
56
+ - lib/capistrano-deploy-management/rvm.rb
57
+ - lib/capistrano-deploy-management/unicorn.rb
58
+ - lib/capistrano-deploy-management/whenever.rb
59
+ - spec/.DS_Store
60
+ - spec/bundle_spec.rb
61
+ - spec/deploy_spec.rb
62
+ - spec/git_spec.rb
63
+ - spec/multistage_spec.rb
64
+ - spec/passenger_spec.rb
65
+ - spec/rails_assets_spec.rb
66
+ - spec/rails_spec.rb
67
+ - spec/spec_helper.rb
68
+ - spec/unicorn_spec.rb
69
+ - spec/whenever_spec.rb
70
+ homepage: https://github.com/drdla/capistrano-deploy-management
71
+ licenses: []
72
+ post_install_message:
73
+ rdoc_options: []
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ! '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ requirements: []
89
+ rubyforge_project:
90
+ rubygems_version: 1.8.24
91
+ signing_key:
92
+ specification_version: 3
93
+ summary: Capistrano deploy recipes
94
+ test_files:
95
+ - spec/bundle_spec.rb
96
+ - spec/deploy_spec.rb
97
+ - spec/git_spec.rb
98
+ - spec/multistage_spec.rb
99
+ - spec/passenger_spec.rb
100
+ - spec/rails_assets_spec.rb
101
+ - spec/rails_spec.rb
102
+ - spec/spec_helper.rb
103
+ - spec/unicorn_spec.rb
104
+ - spec/whenever_spec.rb