capistrano-fanfare 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,87 @@
1
+ require 'capistrano'
2
+ require 'capistrano/recipes/deploy/strategy/git_style'
3
+
4
+ module Capistrano::Fanfare::GitStyle
5
+ def self.load_into(configuration)
6
+ configuration.load do
7
+ set :scm, :git
8
+ set :deploy_via, :git_style
9
+ set(:release_name) { %{#{Time.now.utc.strftime("%Y%m%d%H%M%S")}-#{real_revision}} }
10
+ set(:release_path) { current_path }
11
+ set(:current_release) { current_path }
12
+ set(:latest_release) { current_path }
13
+
14
+ set(:current_revision) {
15
+ capture("cd #{current_path} && git rev-parse HEAD",
16
+ :except => { :no_release => true }).chomp }
17
+ set(:latest_revision) {
18
+ last_release_dir = releases.length > 0 ? File.join(releases_path, releases.last) : nil
19
+ capture("basename #{last_release_dir} | cut -d - -f 2",
20
+ :except => { :no_release => true }).chomp }
21
+ set(:previous_revision) {
22
+ capture("basename #{previous_release} | cut -d - -f 2",
23
+ :except => { :no_release => true }).chomp if previous_release }
24
+
25
+ namespace :deploy do
26
+ desc <<-DESC
27
+ Copies your project to the remote servers. This is the first stage \
28
+ of any deployment; moving your updated code and assets to the deployment \
29
+ servers. You will rarely call this task directly, however; instead, you \
30
+ should call the `deploy' task (to do a complete deploy) or the `update' \
31
+ task (if you want to perform the `restart' task separately).
32
+
33
+ You will need to make sure you set the :scm variable to the source \
34
+ control software you are using (it defaults to :subversion), and the \
35
+ :deploy_via variable to the strategy you want to use to deploy (it \
36
+ defaults to :checkout).
37
+
38
+ [NOTE] This overrides the capistrano default by removing the \
39
+ on_rollback logic since previous release checkouts don't exist.
40
+ DESC
41
+ task :update_code, :except => { :no_release => true } do
42
+ strategy.deploy!
43
+ finalize_update
44
+ end
45
+
46
+ desc <<-DESC
47
+ [internal] No-op for git-based deployments.
48
+
49
+ [NOTE] This overides the capistrano default since there is no need for a
50
+ symlink farm.
51
+ DESC
52
+ task :create_symlink, :except => { :no_release => true } do
53
+ end
54
+
55
+ namespace :rollback do
56
+ desc <<-DESC
57
+ [internal] Updates git HEAD to the last deployed commit.
58
+ This is called by the rollback sequence, and should rarely (if
59
+ ever) need to be called directly.
60
+ DESC
61
+ task :revision, :except => { :no_release => true } do
62
+ if previous_release
63
+ set :branch, previous_revision
64
+ update_code
65
+ else
66
+ raise "could not rollback the code because there is no prior release"
67
+ end
68
+ end
69
+
70
+ desc <<-DESC
71
+ [internal] No-op for git-based deployments.
72
+ This is called by the rollback sequence, and should rarely
73
+ (if ever) need to be called directly.
74
+ DESC
75
+ task :cleanup, :roles => [:app, :web, :db], :except => { :no_release => true } do
76
+ run %{if [ `(cd #{current_path} && git rev-parse HEAD)` != `#{latest_revision}` ]; then rm -rf #{current_release}; fi}
77
+ end
78
+ end
79
+
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ if Capistrano::Configuration.instance
86
+ Capistrano::Fanfare::GitStyle.load_into(Capistrano::Configuration.instance)
87
+ end
@@ -0,0 +1,36 @@
1
+ require 'capistrano'
2
+
3
+ module Capistrano::Fanfare::Multistage
4
+ def self.load_into(configuration)
5
+ configuration.load do
6
+ def _cset(name, *args, &block)
7
+ unless exists?(name)
8
+ set(name, *args, &block)
9
+ end
10
+ end
11
+
12
+ _cset :stages, %w{staging production}
13
+ _cset :default_stage, "staging"
14
+
15
+ require 'capistrano/ext/multistage'
16
+
17
+ # =========================================================================
18
+ # These are the tasks that are available to help with deploying web apps,
19
+ # and specifically, Rails applications. You can have cap give you a summary
20
+ # of them with `cap -T'.
21
+ # =========================================================================
22
+
23
+ desc <<-DESC
24
+ Lists all valid deployment environments.
25
+ DESC
26
+ task :all_stages, :roles => :app, :except => { :no_release => true } do
27
+ logger.important "Valid stages are:\n\n"
28
+ fetch(:stages, []).each { |s| logger.important "* #{s}" }
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ if Capistrano::Configuration.instance
35
+ Capistrano::Fanfare::Multistage.load_into(Capistrano::Configuration.instance)
36
+ end
@@ -0,0 +1,5 @@
1
+ module Capistrano
2
+ module Fanfare
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,22 @@
1
+ require "capistrano"
2
+ require "capistrano/fanfare/version"
3
+
4
+ if Capistrano::Configuration.instance
5
+ Capistrano::Configuration.instance.load_paths << File.dirname(__FILE__)
6
+ end
7
+
8
+ module Capistrano
9
+ module Fanfare
10
+ module Configuration
11
+ def fanfare_recipe(recipe)
12
+ require "capistrano/fanfare/#{recipe}"
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ module Capistrano
19
+ class Configuration
20
+ include Capistrano::Fanfare::Configuration
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ require 'capistrano/recipes/deploy/strategy/remote'
2
+
3
+ module Capistrano
4
+ module Deploy
5
+ module Strategy
6
+
7
+ class GitStyle < Remote
8
+ protected
9
+
10
+ def command
11
+ @command ||= "if [ -d #{configuration[:current_path]}/.git ]; then " +
12
+ "#{source.sync(revision, configuration[:current_path])}; " +
13
+ "else #{source.checkout(revision, configuration[:current_path])}; fi"
14
+ end
15
+
16
+ def mark
17
+ "(mkdir -p #{File.join(configuration[:releases_path], configuration[:release_name])})"
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,162 @@
1
+ require 'minitest/autorun'
2
+ require 'minitest/capistrano'
3
+ require 'capistrano/fanfare'
4
+ require 'capistrano/fanfare/bundler'
5
+
6
+ #
7
+ # Rake mixes in FileUtils methods into Capistrano::Configuration::Namespace as
8
+ # private methods which will cause a method/task namespace collision when the
9
+ # `bundle:install' task is created.
10
+ #
11
+ # So, if we are in a Rake context, nuke :install in the Namespace class--we
12
+ # won't be using it directly in this codebase but this feels so very, very
13
+ # wrong (here be dragons).
14
+ #
15
+ if defined?(Rake::DSL)
16
+ Capistrano::Configuration::Namespaces::Namespace.class_eval { undef :install }
17
+ end
18
+
19
+ describe Capistrano::Fanfare::Bundler do
20
+ before do
21
+ @config = Capistrano::Configuration.new
22
+ Capistrano::Fanfare::Bundler.load_into(@config)
23
+ @config.extend(MiniTest::Capistrano::ConfigurationExtension)
24
+ @orig_config = Capistrano::Configuration.instance
25
+ Capistrano::Configuration.instance = @config
26
+
27
+ @config.set :current_release, "/srv/gemmy/releases/thisone"
28
+ end
29
+
30
+ after do
31
+ Capistrano::Configuration.instance = @orig_config
32
+ end
33
+
34
+ describe "for variables" do
35
+ it "sets :bundle_cmd to 'bundle'" do
36
+ @config.fetch(:bundle_cmd).must_equal "bundle"
37
+ end
38
+
39
+ it "sets :bundle_shebang to 'ruby-local-exec'" do
40
+ @config.fetch(:bundle_shebang).must_equal "ruby-local-exec"
41
+ end
42
+
43
+ it "add :current_path/bin to the default_environment PATH" do
44
+ @config.set :current_path, "/tmp/app/current"
45
+
46
+ @config.fetch(:default_environment)['PATH'].must_equal "/tmp/app/current/bin:$PATH"
47
+ end
48
+
49
+ it "sets :bundle_binstub_template to the binstub script" do
50
+ @config.set :bundle_shebang, "jruby"
51
+
52
+ @config.fetch(:bundle_binstub_template).must_equal <<-BINSTUB
53
+ #!/usr/bin/env jruby
54
+ #
55
+ # This file was generated by capistrano.
56
+ #
57
+
58
+ require 'pathname'
59
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
60
+ Pathname.new(__FILE__).realpath)
61
+
62
+ require 'rubygems'
63
+
64
+ load Gem.bin_path('bundler', 'bundle')
65
+ BINSTUB
66
+ end
67
+
68
+ it "sets :rake to 'rake'" do
69
+ @config.fetch(:rake).must_equal "rake"
70
+ end
71
+
72
+ describe ":bundle_flags" do
73
+ after do
74
+ ENV.delete('VERBOSE')
75
+ end
76
+
77
+ it "contains --deployment" do
78
+ @config.fetch(:bundle_flags).must_match /--deployment/
79
+ end
80
+
81
+ it "contains --binstubs" do
82
+ @config.fetch(:bundle_flags).must_match /--binstubs/
83
+ end
84
+
85
+ it "contains --shebang <shebang_bin>" do
86
+ @config.set :bundle_shebang, "bangbang"
87
+
88
+ @config.fetch(:bundle_flags).must_match /--shebang bangbang/
89
+ end
90
+
91
+ it "contains --quiet by default" do
92
+ @config.fetch(:bundle_flags).must_match /--quiet/
93
+ end
94
+
95
+ it "does not contain --quiet if ENV['VERSBOSE'] is set" do
96
+ ENV['VERBOSE'] = "yes"
97
+
98
+ @config.fetch(:bundle_flags).wont_match /--quiet/
99
+ end
100
+ end
101
+
102
+ describe ":bundle_without" do
103
+ it "contains :development and :test groups" do
104
+ @config.fetch(:bundle_without).must_include :development
105
+ @config.fetch(:bundle_without).must_include :test
106
+ end
107
+
108
+ it "contains all other values from :os_types if the :os_type variable exists" do
109
+ @config.set :os_types, [:fizz, :buzz, :rocketships]
110
+ @config.set :os_type, :rocketshipos
111
+
112
+ @config.fetch(:bundle_without).must_include :fizz
113
+ @config.fetch(:bundle_without).must_include :buzz
114
+ @config.fetch(:bundle_without).must_include :development
115
+ @config.fetch(:bundle_without).must_include :test
116
+ end
117
+ end
118
+ end
119
+
120
+ describe "for namespace :bundle" do
121
+ it "creates a bundle:install task" do
122
+ @config.must_have_task "bundle:install"
123
+ end
124
+
125
+ it "calls bundle:install task after deploy:finalize_update" do
126
+ @config.must_have_callback_after "deploy:finalize_update", "bundle:install"
127
+ end
128
+
129
+ describe "task :create_binstub_script" do
130
+ it "creates bin/bundle binstub script" do
131
+ @config.set :shared_path, "/tmp/app/shared"
132
+ @config.set :bundle_binstub_template, "thescript"
133
+ @config.find_and_execute_task("bundle:create_binstub_script")
134
+
135
+ @config.must_have_run "mkdir -p /tmp/app/shared/bin"
136
+ @config.must_have_put "/tmp/app/shared/bin/bundle", "thescript"
137
+ end
138
+
139
+ it "gets called after deploy:setup task" do
140
+ @config.must_have_callback_after "deploy:setup", "bundle:create_binstub_script"
141
+ end
142
+ end
143
+
144
+ describe "task :cp_bundle_binstub" do
145
+ it "copies bin/bundle into current_path" do
146
+ @config.set :shared_path, "/tmp/app/shared"
147
+ @config.set :current_path, "/tmp/app/current"
148
+ @config.find_and_execute_task("bundle:cp_bundle_binstub")
149
+
150
+ @config.must_have_run [
151
+ "mkdir -p /tmp/app/current/bin",
152
+ "cp /tmp/app/shared/bin/bundle /tmp/app/current/bin/bundle",
153
+ "chmod 0755 /tmp/app/current/bin/bundle"
154
+ ].join(" && ")
155
+ end
156
+
157
+ it "gets called after deploy:update_code task" do
158
+ @config.must_have_callback_before "deploy:finalize_update", "bundle:cp_bundle_binstub"
159
+ end
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,177 @@
1
+ require 'minitest/autorun'
2
+ require 'minitest/capistrano'
3
+ require 'capistrano/fanfare'
4
+ require 'capistrano/fanfare/defaults'
5
+
6
+ describe Capistrano::Fanfare::Defaults do
7
+ before do
8
+ @config = Capistrano::Configuration.new
9
+ Capistrano::Fanfare::Defaults.load_into(@config)
10
+ @config.extend(MiniTest::Capistrano::ConfigurationExtension)
11
+ ENV['BRANCH'] = nil
12
+ end
13
+
14
+ it "sets :scm to :git" do
15
+ @config.fetch(:scm).must_equal :git
16
+ end
17
+
18
+ it "sets :use_sudo to false" do
19
+ @config.fetch(:use_sudo).must_equal false
20
+ end
21
+
22
+ it "sets :user to 'deploy'" do
23
+ @config.fetch(:user).must_equal "deploy"
24
+ end
25
+
26
+ it "sets :rake to 'bundle exec rake'" do
27
+ @config.fetch(:rake).must_equal "bundle exec rake"
28
+ end
29
+
30
+ it "sets :ssh_options to include :forward_agent => true" do
31
+ @config.fetch(:ssh_options)[:forward_agent].must_equal true
32
+ end
33
+
34
+ it "sets :deploy_to to a directory containing :application and :deploy_env" do
35
+ @config.set :application, "fizzy"
36
+ @config.set :deploy_env, "staging"
37
+
38
+ @config.fetch(:deploy_to).must_equal "/srv/fizzy_staging"
39
+ end
40
+
41
+ describe ":branch" do
42
+ it "sets to master by default" do
43
+ @config.fetch(:branch).must_equal "master"
44
+ end
45
+
46
+ it "sets to ENV['BRANCH'] if set" do
47
+ ENV['BRANCH'] = "tree-branch"
48
+
49
+ @config.fetch(:branch).must_equal "tree-branch"
50
+ end
51
+ end
52
+
53
+ describe ":deploy_env" do
54
+ before do
55
+ @config.set :stage, "stage_env"
56
+ @config.set :rails_env, "railsy_baby"
57
+ @config.set :rack_env, "rackish_dude"
58
+
59
+ ENV['RAILS_ENV'] = "rails_env_hash"
60
+ ENV['RACK_ENV'] = "rack_env_hash"
61
+ end
62
+
63
+ after do
64
+ ENV.delete 'RAILS_ENV'
65
+ ENV.delete 'RACK_ENV'
66
+ end
67
+
68
+ it "sets to :stage if it exists" do
69
+ @config.fetch(:deploy_env).must_equal "stage_env"
70
+ end
71
+
72
+ it "set to :rails_env if :stage isn't present" do
73
+ @config.unset :stage
74
+
75
+ @config.fetch(:deploy_env).must_equal "railsy_baby"
76
+ end
77
+
78
+ it "set to ENV['RAILS_ENV'] if :stage and :rails_env arent't present" do
79
+ @config.unset :stage
80
+ @config.unset :rails_env
81
+
82
+ @config.fetch(:deploy_env).must_equal "rails_env_hash"
83
+ end
84
+
85
+ it "set to :rack_env if :stage, :rails_env, and ENV['RAILS_ENV'] arent't present" do
86
+ @config.unset :stage
87
+ @config.unset :rails_env
88
+ ENV.delete 'RAILS_ENV'
89
+
90
+ @config.fetch(:deploy_env).must_equal "rackish_dude"
91
+ end
92
+
93
+ it "set to ENV['RACK_ENV'] if :stage, :rails_env, ENV['RAILS_ENV'], and :rack_env arent't present" do
94
+ @config.unset :stage
95
+ @config.unset :rails_env
96
+ @config.unset :rack_env
97
+ ENV.delete 'RAILS_ENV'
98
+
99
+ @config.fetch(:deploy_env).must_equal "rack_env_hash"
100
+ end
101
+
102
+ it "set to 'production' as a fallback" do
103
+ @config.unset :stage
104
+ @config.unset :rails_env
105
+ @config.unset :rack_env
106
+ ENV.delete 'RAILS_ENV'
107
+ ENV.delete 'RACK_ENV'
108
+
109
+ @config.fetch(:deploy_env).must_equal "production"
110
+ end
111
+ end
112
+
113
+ it "sets :pty = true for default_run_options" do
114
+ @config.default_run_options[:pty].must_equal true
115
+ end
116
+
117
+ it "sets :os_types to a list of OSes" do
118
+ @config.fetch(:os_types).must_equal [:darwin, :linux, :sunos]
119
+ end
120
+
121
+ describe ":os_type" do
122
+ it "set to :darwin for Mac OS" do
123
+ @config.captures_responses["uname -s"] = "Darwin\n"
124
+
125
+ @config.fetch(:os_type).must_equal :darwin
126
+ end
127
+
128
+ it "set to :linunx for Linux-based OSes" do
129
+ @config.captures_responses["uname -s"] = "Linux\n"
130
+
131
+ @config.fetch(:os_type).must_equal :linux
132
+ end
133
+
134
+ it "set to :sunos for Solaris-based OSes" do
135
+ @config.captures_responses["uname -s"] = "SunOS\n"
136
+
137
+ @config.fetch(:os_type).must_equal :sunos
138
+ end
139
+ end
140
+
141
+ it "sets :shared_children to include tmp/sockets and tmp/sessions" do
142
+ @config.fetch(:shared_children).
143
+ must_equal %w{public/system log tmp/pids tmp/sockets tmp/sessions}
144
+ end
145
+
146
+ describe "for :deploy namespace" do
147
+ describe "task :cold" do
148
+ before do
149
+ @config.load do
150
+ def methods_called ; @methods_called ||= [] ; end
151
+
152
+ namespace :deploy do
153
+ task(:update) { methods_called << "deploy:update" }
154
+ task(:migrate) { methods_called << "deploy:migrate" }
155
+ task(:start) { methods_called << "deploy:start" }
156
+ end
157
+ end
158
+ end
159
+
160
+ it "calls same tasks as delivered gem code" do
161
+ @config.find_and_execute_task("deploy:cold")
162
+
163
+ @config.methods_called.must_equal(
164
+ ["deploy:update", "deploy:migrate", "deploy:start"])
165
+ end
166
+
167
+ it "calls db:seed if the task exists" do
168
+ @config.namespace :db do
169
+ task(:seed) { methods_called << "db:seed" }
170
+ end
171
+ @config.find_and_execute_task("deploy:cold")
172
+
173
+ @config.methods_called.must_include "db:seed"
174
+ end
175
+ end
176
+ end
177
+ end