mirror of
https://github.com/capistrano/capistrano
synced 2023-03-27 23:21:18 -04:00
1324 lines
60 KiB
HTML
1324 lines
60 KiB
HTML
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Capistrano Version 3 Release Announcement</title>
|
||
<link href='http://fonts.googleapis.com/css?family=Enriqueta' rel='stylesheet' type='text/css'>
|
||
<script type="text/javascript" src="//use.typekit.net/itm5ubu.js"></script>
|
||
<script type="text/javascript">try{Typekit.load();}catch(e){}</script>
|
||
<link rel="stylesheet" href="/css/foundation.css" />
|
||
<link rel="stylesheet" href="/css/capistrano.css">
|
||
<link rel="stylesheet" href="/css/social_foundicons.css" />
|
||
<link rel="stylesheet" href="/css/okaidia.css">
|
||
<script src="/js/vendor/custom.modernizr.js"></script>
|
||
</head>
|
||
<body>
|
||
|
||
<script type="text/javascript">
|
||
var _gauges = _gauges || [];
|
||
(function() {
|
||
var t = document.createElement('script');
|
||
t.type = 'text/javascript';
|
||
t.async = true;
|
||
t.id = 'gauges-tracker';
|
||
t.setAttribute('data-site-id', '51c83c32613f5d7df70000bc');
|
||
t.src = '//secure.gaug.es/track.js';
|
||
var s = document.getElementsByTagName('script')[0];
|
||
s.parentNode.insertBefore(t, s);
|
||
})();
|
||
</script>
|
||
|
||
<script type="text/javascript">
|
||
setTimeout(function(){var a=document.createElement("script");
|
||
var b=document.getElementsByTagName("script")[0];
|
||
a.src=document.location.protocol+"//dnn506yrbagrg.cloudfront.net/pages/scripts/0017/6418.js?"+Math.floor(new Date().getTime()/3600000);
|
||
a.async=true;a.type="text/javascript";b.parentNode.insertBefore(a,b)}, 1);
|
||
</script>
|
||
|
||
<script>
|
||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||
|
||
ga('create', 'UA-41970098-1', 'capistranorb.com');
|
||
ga('send', 'pageview');
|
||
</script>
|
||
<div class="header">
|
||
<div class="row">
|
||
<div class="large-12 column">
|
||
<a href="/" class="brand">
|
||
<img src="/images/CapistranoLogo.png" />
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<div class="row">
|
||
<div class="large-4 columns">
|
||
<ul class="side-nav">
|
||
<li><a href="https://www.harrow.io/" class="advertisment"><span class="label label-important">New</span> Hosted Capistrano for Teams</a></li>
|
||
<li class="divider"></li>
|
||
<h5>Overview</h5>
|
||
<li><a href="/documentation/overview/what-is-capistrano/">What is Capistrano?</a></li>
|
||
<!--<li><a href="/documentation/overview/introductory-demo-video/">Introductory Demo Video</a></li>-->
|
||
<li class="divider"></li>
|
||
<h5>Getting Started</h5>
|
||
<li><a href="https://github.com/capistrano/capistrano/blob/v3/README.md">The Readme, start here!</a></li>
|
||
<li><a href="/documentation/getting-started/installation/">Installation</a></li>
|
||
<li><a href="/documentation/getting-started/preparing-your-application/">Preparing Your Application</a></li>
|
||
<li><a href="/documentation/getting-started/authentication-and-authorisation/">Authentication & Authorisation</a></li>
|
||
<li><a href="/documentation/getting-started/cold-start/">Cold Start</a></li>
|
||
<li><a href="/documentation/getting-started/flow/">Flow</a></li>
|
||
<li><a href="/documentation/getting-started/rollbacks/">Rollbacks</a></li>
|
||
<li><a href="/documentation/getting-started/remote-file/">Remote file task</a></li>
|
||
<li class="divider"></li>
|
||
<h5>Framework Extensions</h5>
|
||
<li><a href="/documentation/frameworks/ruby-on-rails/">Ruby on Rails</a></li>
|
||
<li><a href="/documentation/frameworks/bundler/">Bundler</a></li>
|
||
<li><a href="/documentation/frameworks/rbenv-rvm-chruby/">Rbenv & RVM & chruby</a></li>
|
||
<!--<li class="divider"></li> -->
|
||
<!--<h5>Troubleshooting</h5> -->
|
||
<!--<li><a href="/documentation/troubleshooting/authentication/">SCM (Git) Authentication</a></li>-->
|
||
<!--<li><a href="/documentation/troubleshooting/connectivity/">Connectivity</a></li> -->
|
||
<!--[><li><a href="/documentation/troubleshooting/gateway-servers/">Gateway Servers</a></li><] -->
|
||
<!--<li><a href="/documentation/troubleshooting/agent-forwarding/">Agent Forwarding</a></li> -->
|
||
<!--<li><a href="/documentation/troubleshooting/sudo-password/">`sudo` Password</a></li> -->
|
||
<!--<li><a href="/documentation/troubleshooting/rvm-rbenv-nvm/">RVM, `rbenv` And `nvm`</a></li> -->
|
||
<li class="divider"></li>
|
||
<h5>FAQ</h5>
|
||
<li><a href="/documentation/faq/why-does-something-work-in-my-ssh-session-but-not-in-capistrano/">Why Does Something Work In An SSH Session, But Not In Capistrano?</a></li>
|
||
<!--<li><a href="/documentation/faq/should-i-use-capistrano-to-provision-my-servers/">Should I Use Capistrano To Provision My Servers?</a></li>-->
|
||
<li><a href="http://lee.hambley.name/2013/06/11/using-capistrano-v3-with-chef.html">Should I Use Capistrano To Provision My Servers?</a></li>
|
||
<!--<li class="divider"></li> -->
|
||
<!--<h5>Power Use-Cases</h5> -->
|
||
<!--<li><a href="/documentation/power-use-cases/integration-with-rake/">Integration With Rake</a></li> -->
|
||
<!--<li><a href="/documentation/power-use-cases/driving-tools-such-as-chef-solo/">Driving Tools Such As <em>Chef Solo</em></a></li>-->
|
||
<li class="divider"></li>
|
||
<h5>Legacy Documentation</h5>
|
||
<li><a href="https://github.com/capistrano/capistrano/wiki">Capistrano
|
||
v2</a></li>
|
||
<li class="divider"></li>
|
||
<h5>Recent Announcements</h5>
|
||
|
||
<li><a href="/2013/06/01/release-announcement.html"><span class="post-date">01 Jun 2013</span> Capistrano Version 3 Release Announcement</a></li>
|
||
|
||
</ul>
|
||
|
||
</div>
|
||
<div class="large-8 column">
|
||
<div class="content">
|
||
<h2>Capistrano Version 3 Release Announcement</h2>
|
||
<p class="meta">01 Jun 2013</p>
|
||
|
||
<div id="post">
|
||
<p>After what seems like years of work, the Capistrano team (that's Tom and I)
|
||
are pleased to announce the first <em>major</em> release of Capistrano in almost 5
|
||
years.</p>
|
||
|
||
<p>The reasons behind the length of time between the last architectural overhaul
|
||
and this one are numerous, but it can be summrised to say that Capistrano is a
|
||
widely used tool, and when working around software deployment it's really a
|
||
question of downtime. If we had changed something significant in Capistrano we
|
||
could have taken a lot of sites offline, and made a lot of people very
|
||
unhappy. Until this point we haven't felt that the time has been ripe where
|
||
the benefits of a slightly rocky upgrade path are worth the risks of downtime.</p>
|
||
|
||
<p>It also hasn't helped historically that we've only just gotten to grips with
|
||
Ruby 1.9, and that Bundler's near ubuquity means that now it's trivial to lock
|
||
a Gem at a specific version. With other tools in the Ruby eco system it's
|
||
become easier for us to make significant changes to a tool upon which many
|
||
hundreds of thousands of people rely.</p>
|
||
|
||
<h3 id="toc_0">Design Goals</h3>
|
||
|
||
<p>We had a few goals for this release, in no particular order they were:</p>
|
||
|
||
<ul>
|
||
<li><strong>Get away from our own DSL solution.</strong> Great DSL alternatives (Rake, Sake, Thor,
|
||
etc) are already widely used.</li>
|
||
<li><strong>Better modularisation.</strong> to enable people outside the Rails community to
|
||
benefit from Capistrano's <em>best-practice</em> workflow, and to enable people in
|
||
the Rails community to pick and choose support for components they use
|
||
(Database Migrations, Asset Pipeline, etc)</li>
|
||
<li><strong>Easier Debugging.</strong> A lot of problems with Capistrano come from weirdness
|
||
surrounding environmental issues around PTY vs non-TTY environments, login
|
||
and non-login shells not to mention <em>environment managers</em> such as rvm,
|
||
rbenv and nvm.</li>
|
||
<li><strong>Speed.</strong> We know that in a lot of environments speed of deployment is a
|
||
huge factor, since Rails introduced the <em>Asset Pipeline</em> it's not uncommon
|
||
for a deploy that formerly took 5 seconds now takes 5 minutes. This really
|
||
is mostly out of our control, but with improved support for parallelism,
|
||
rolling restarts we feel confident that things will be quicker and easier to
|
||
keep running quickly now.</li>
|
||
<li><strong>Applicability.</strong> We've always maintained that Capistrano is a terrible
|
||
tool for system provisioning, and that more often than not servers are
|
||
better being setup with Chef, Puppet or similar, whilst we still agree with
|
||
that, the new features in Capistrano really lend themselves to integrating
|
||
with these kinds of tools.</li>
|
||
</ul>
|
||
|
||
<h3 id="toc_1">What's missing?</h3>
|
||
|
||
<p>Before we get too carried away it's worth shortlisting the things that don't
|
||
exist in version three, <strong><em>yet</em></strong>.</p>
|
||
|
||
<ul>
|
||
<li><strong>SSH Gateway Support</strong> SSH Gateway support hasn't been implemented in
|
||
version three yet, I hope that this will be done soon. As I have no direct
|
||
need for it, I haven't the means to test it with a view to implementing it,
|
||
yet.</li>
|
||
<li><strong>Mecurial, Subversion, and CVS Support</strong> These have been removed as we've
|
||
been able to implement the Git SCM in an incredibly neat way that isn't
|
||
compatible with the others. We wanted to break the cycle of always sticking
|
||
with the lowest common denominator, so we are <strong>actively</strong> looking for
|
||
people who are interested in contributing, or sharing expertise on the
|
||
<em>best-practice</em> way of speedily deploying from your respective choice of
|
||
source control.</li>
|
||
<li>**<code>HOSTFILTER</code> ,<code>ROLEFILTER</code> and friends** These have gone away because we
|
||
always felt they were indemic of a bad design desision about using
|
||
Environmental Variables. These will be coming back as flags passed to <code>cap</code>
|
||
on the CLI, and options that can be set on the Capistrano::Application Ruby
|
||
class.</li>
|
||
<li><strong>Shell</strong> The shell has been removed temporarily pending a neater
|
||
implementation, we've got something that we are playing with internally, but
|
||
it needs better readline support, and some more controls around what to do
|
||
when things go badly on some servers, but not others.</li>
|
||
<li><strong>Cold Deploy</strong> The <code>cap deploy:cold</code> is a really old legacy component,
|
||
orignally from the days of the <code>script/spinner</code> where deploying cold
|
||
(starting workers that weren't running), and deploying a <em>warm</em> system were
|
||
different (restarting existing worker pools, which wasn't fun!) By and large
|
||
these things have gone away, and it's time <code>deploy:cold</code> went away. It's
|
||
safe in every case we could find to call setup, and seed and other Rake
|
||
tasks without things blowing up, and that should be the approach we take.
|
||
Tasks on the server should be idempotent, and if something is called twice,
|
||
let it be.</li>
|
||
</ul>
|
||
|
||
<h3 id="toc_2">What's new?</h3>
|
||
|
||
<p>Each section here really deserves it's own sub-heading as some of the new
|
||
features are awesome.</p>
|
||
|
||
<h4 id="toc_3">Rake Integration</h4>
|
||
|
||
<p>We have moved away from our own DSL implemenation to implement Capistrano as a
|
||
<em>Rake</em> application.</p>
|
||
|
||
<p>Rake has always supported being sub-classed, so to speak as a
|
||
<em>sub-application</em>; it is however poorly documented. By subclassing
|
||
<code>Rake::Application</code> one can specify what the <em>Rakefile</em> should look like, where
|
||
to search for it, and how to load other <em>Rakefiles</em>.</p>
|
||
|
||
<p>The <em>Rake</em> DSL is widely used, well known and very powerful. As Rake is
|
||
essentially a dependency resolution system, it offers a lot of nice ways to,
|
||
for example build a tarball as a dependency of uploading it and deploying it.</p>
|
||
|
||
<p>This has allowed us to do away with the <em>copy</em> strategy all together, as it
|
||
can now be implemented from scratch in fewer than ten lines of code. You can
|
||
check out the <a href="/screencasts/replicating_the_copy_strategy">replicating the copy
|
||
strategy</a> screencast and
|
||
acompanying documentation if you want to explore that any further.</p>
|
||
|
||
<p>The guiding principle is dependency resolution, and interoperability with
|
||
other tools, for example:</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'># Capistrano 3.0.x
|
||
task :notify do
|
||
this_release_tag = sh("git describe --abbrev=0 --tags")
|
||
last_ten_commits = sh("git log #{this_release_tag}~10..#{this_release_tag}")
|
||
Mail.deliver do
|
||
to "team@example.com"
|
||
subject "Releasing #{this_release_tag} Now!"
|
||
body last_ten_commits
|
||
end
|
||
end
|
||
|
||
namespace :deploy
|
||
task default: :notify
|
||
end</code></pre>
|
||
</div>
|
||
|
||
<p>The last three lines rely on Rake's additive task declaration, by redefining the
|
||
<code>deploy:default</code> task by adding another dependency. Rake will automatically
|
||
resolve this dependency at Runtime, mailing the recent changelog to your team,
|
||
assuming everything is setup correctly.</p>
|
||
|
||
<h4 id="toc_4">Built In Stage Support</h4>
|
||
|
||
<p>In former versions of Capistrano <em>stage</em> support was an after thought,
|
||
provided through the <code>capistrano-ext</code> Gem, and latterly merged into the main
|
||
codebase, people insisted in still using the <code>capistrano-ext</code> version
|
||
regardless.</p>
|
||
|
||
<p>In Capistrano 3.0.x there's stage support built in, at installation time, two
|
||
stages will be created by default, <em>staging</em> and <em>production</em>; it's easy to
|
||
add more, just add a file to <code>config/deploy/______.rb</code> which follows the
|
||
conventions established in the examples we created for you.</p>
|
||
|
||
<p>To create different stages at installation time, simply set the <code>STAGES</code>
|
||
environmental variable to a comma separated list of stages:</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-bash'>$ cap install STAGES=staging,production,ci,qa</code></pre>
|
||
</div>
|
||
|
||
<h4 id="toc_5">Parallelism</h4>
|
||
|
||
<p>In former versions of Capistrano there was a <em>parallel</em> option to run
|
||
different tasks differently on groups of servers, it looked something like
|
||
this:</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'># Capistrano 2.0.x
|
||
task :restart do
|
||
parallel do |session|
|
||
session.when "in?(:app)", "/u/apps/social/script/restart-mongrel"
|
||
session.when "in?(:web)", "/u/apps/social/script/restart-apache"
|
||
session.else "echo nothing to do"
|
||
end
|
||
end</code></pre>
|
||
</div>
|
||
|
||
<p>This always felt a little unclean, and indeed it's a hack that was originally
|
||
implemeted to facilitate rolling deployments at a large German firm by a
|
||
couple of freelancers who were consulting with them. (Hint, one of those guys
|
||
went on to found Travis-CI!)</p>
|
||
|
||
<p>The equivilent code in under Capistrano v3 would look like this:</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'># Capistrano 3.0.x
|
||
task :restart do
|
||
on :all, in: :parallel do |host|
|
||
if host.roles.include?(:app)
|
||
execute "/u/apps/social/script/restart-mongrel"
|
||
elsif host.roles.include?(:web)
|
||
execute "/u/apps/social/script/restart-web"
|
||
else
|
||
info sprintf("Nothing to do for %s with roles %s", host,
|
||
host.properties.roles)
|
||
end
|
||
end
|
||
end</code></pre>
|
||
</div>
|
||
|
||
<p>The second block of code, that representing the new Rake derived DSL and
|
||
demonstrating how to use the parallel execution mode is a little longer, but I
|
||
think it's clearer, more idiomatic Ruby code which relies less on an intimate
|
||
knowledge of how the Capistrano DSL happens to work. It also hints at the
|
||
built-in logging subsystem, keep reading to learn more.</p>
|
||
|
||
<p>Other modes for parallelism include:</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'># Capistrano 3.0.x
|
||
on :all, in: :groups, max: 3, wait: 5 do
|
||
# Take all servers, in groups of three which execute in parallel
|
||
# wait five seconds between groups of servers.
|
||
# This is perfect for rolling restarts
|
||
end
|
||
|
||
on :all, in: :sequence, wait: 15 do
|
||
# This takes all servers, in sequence and waits 15 seconds between
|
||
# each server, this might be perfect if you are afraid about
|
||
# overloading a shared resource, or want to defer the asset compilation
|
||
# over your cluster owing to worries about load
|
||
end
|
||
|
||
on :all, in: :parallel do
|
||
# This will simply try and execute the commands contained within
|
||
# the block in parallel on all servers. This might be perfect for kicking
|
||
# off something like a Git checkout or similar.
|
||
end</code></pre>
|
||
</div>
|
||
|
||
<p>The internal tasks, for standard deploy recipes make use of all of these as is
|
||
appropriate for the normal case, no need to be afraid of scary slow deploys
|
||
again!</p>
|
||
|
||
<h4 id="toc_6">Streaming IO</h4>
|
||
|
||
<p>This IO streaming model means that results from commands, the commands
|
||
themselves and any other arbitrary output are sent as objects to a class with
|
||
an <code>IO</code>ish interface, the class knows what to do with these things. There's a
|
||
<em>progress</em> formatter which prints dots for each command that is called, as
|
||
well as a <em>pretty</em> formatter which prints the full command, it's output on
|
||
standard out and standard error, as well as the final return status. It would
|
||
be trivial to implement HTML formatters, or formatters that reported to your
|
||
IRC room, or to email. I look forward to seeing more of these cropping up in
|
||
the community.</p>
|
||
|
||
<h4 id="toc_7">Host Definition Access</h4>
|
||
|
||
<p>If you didn't skim over the <em>Parallism</em> section above, you might have noticed we
|
||
did something clever that wasn't possible in Capistrano v2; we accessed the
|
||
<code>host</code> inside the execution block.</p>
|
||
|
||
<p>For a lot of reasons in Capistrano v2 is wasn't possible to do this, the block
|
||
was essentially evaluated once and called verbatim on each host. This lead to
|
||
disappointing missing features such as not being able to pull the host list
|
||
out of Capistrano and examine the roles to do something like controlling chef
|
||
solo, or similar.</p>
|
||
|
||
<p>In Capistrano v3 the <code>host</code> object is the same object that is created when a
|
||
server is defined, and is internally used, for example to pass to an ERB
|
||
template for rendering a last-deploy message that is dumped onto each server
|
||
after a successful deployment. The last deploy log includes everything
|
||
Capistrano knew about that server during the deployment.</p>
|
||
|
||
<blockquote>
|
||
<p>Users of Capistrano v2 may be familiar with the perenial <code>cap deploy:cleanup</code>
|
||
problem which came to light when servers differed in their old releases list,
|
||
imagine a scenario with two servers, one has been your bread-and-butter since
|
||
you launched, it has hundreds of old releases from all your wonderful deploys
|
||
over the months or years. The second server has been in the cluster for about
|
||
a month, it didn't quite slot-in cleanly, so the list of old releases looks a
|
||
bit weird, you deleted a few by hand, and anyway there might only be ten-or-so
|
||
releases there.</p>
|
||
|
||
<p>Now imagine that you call <code>cap deploy:cleanup</code>, old <code>capture()</code>
|
||
implementations silently only ran on the first server that matched the
|
||
properties defined, so server one returned a list of ~95 old timestamped
|
||
release directories. Next Capistrano v2 would call <code>rm -rf
|
||
release1..release95</code> on <strong>both</strong> servers, causing server two to err our, and
|
||
leaving an undefined state on server one, as Capistrano would simply hang up
|
||
both connections.</p>
|
||
</blockquote>
|
||
|
||
<p>This cleanup routine can now be better implemented as follows (which is
|
||
actually more or less the actual implementation in the the new Gem):</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'># Capistrano 3.0.x
|
||
desc "Cleanup all old releases (keeps #{fetch(:releases_to_keeo_on_cleanup)}
|
||
old releases"
|
||
task :cleanup do
|
||
keep_releases = fetch(:releases_to_keep_on_cleanup)
|
||
releases = capture(:ls, fetch(:releases_directory))
|
||
release_to_delete = releases.sort_by { |r| rn.to_i }.slice(1..-(keep_releases + 1))
|
||
releases_to_delete.each do |r|
|
||
execute :rm, fetch(:releases_directory).join(r)
|
||
end
|
||
end</code></pre>
|
||
</div>
|
||
|
||
<p>Some handy things to note here are that both server one and server two in our
|
||
contrived example will both evaluate that independently, and when both servers
|
||
are finished removing old releases the <code>task :cleanup</code> block will have
|
||
finished.</p>
|
||
|
||
<p>Also in Capistrano v3 most path varaibles are [<code>Pathname</code>] objects, so they natively
|
||
respond to things like <code>#basename</code>, <code>#expand_path</code>, <code>#join</code> and similar.</p>
|
||
|
||
<p><strong>Warning:</strong> <code>#expand_path</code> probably won't do what you expect, it will execute
|
||
on your <em>workstation</em> machine, and not on the remote host, so it's possible
|
||
that it will return an error in the case of paths which exist remotely but not
|
||
locally.</p>
|
||
|
||
<h4 id="toc_8">Host Properties</h4>
|
||
|
||
<p>As the <code>host</code> object is now available to the task blocks, it made sense to make
|
||
it possible to store arbitrarty values against them.</p>
|
||
|
||
<p>Enter <code>host.properties</code>. This is a simple
|
||
<a href="http://www.ruby-doc.org/stdlib-2.0/libdoc/ostruct/rdoc/OpenStruct.html"><em>OpenStruct</em></a>
|
||
which can be used to store any additional properties which are important for
|
||
your application.</p>
|
||
|
||
<p>An example of it's usage might be:</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'>h = SSHKit::Host.new 'example.com'
|
||
h.properties.roles ||= %i{wep app}</code></pre>
|
||
</div>
|
||
|
||
<h4 id="toc_9">More Expressive Command Language</h4>
|
||
|
||
<p>In Capistrano v2, it wasn't uncommon to find commands such as:</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'># Capistrano 2.0.x
|
||
task :precompile, :roles => lambda { assets_role }, :except => { :no_release => true } do
|
||
run <<-CMD.compact
|
||
cd -- #{latest_release} &&
|
||
RAILS_ENV=#{rails_env.to_s.shellescape} #{asset_env} #{rake} assets:precompile
|
||
CMD
|
||
end</code></pre>
|
||
</div>
|
||
|
||
<p>In Capistrano v3 this looks more like this:</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'># Capistrano 3.0.x
|
||
task :precompile do
|
||
on :sprockets_asset_host, reject: lambda { |h| h.properties.no_release } do
|
||
within fetch(:latest_release_directory)
|
||
with rails_env: fetch(:rails_env) do
|
||
execute :rake, 'assets:precompile'
|
||
end
|
||
end
|
||
end
|
||
end</code></pre>
|
||
</div>
|
||
|
||
<p>Again, with other examples this format is a little longer, but much more
|
||
expressive, and all the nightmare of shell escaping is handled interally for
|
||
you, environmental variables are capitalised and applied at the correct point
|
||
(i.e between the <code>cd</code> and <code>rake</code> calls in this case).</p>
|
||
|
||
<p>Other options here include <code>as :a_user</code> and</p>
|
||
|
||
<h4 id="toc_10">Better <em>magic</em> Variable Support</h4>
|
||
|
||
<p>In Capistrano v2 there were certain bits of magic where if calling a variable
|
||
and NoMethodError would have been raised (for example the
|
||
<code>latest_release_directory</code> varaible). This variable never existed on the
|
||
global namespace, as a fall-back the list of <code>set()</code> variables would be
|
||
consulted.</p>
|
||
|
||
<p>This magic lead to times when people were not recognising that magic variables
|
||
were even being used. The magic variable system of Capistrano v2 did also
|
||
include a way to <code>fetch(:some_variable, 'with a default value')</code> incase the
|
||
variable might not be set already, but it wasn't widely used, and more often
|
||
than not people just used things like <code>latest_release_directory</code> never knowing
|
||
that behind the scenes an exception was raised, then rescued, and that
|
||
<code>:latest_release_directory</code> in the variable map was actually a continuation
|
||
that was evaluated the first time it was used, and the value then cached until
|
||
the end of the script.</p>
|
||
|
||
<p>The system has now 100% less magic. If you set a variable using <code>set()</code>, it
|
||
can be fetched with <code>fetch()</code>, if the value you set into the variable responds
|
||
to <code>#call</code> then it will be executed in the current context whenever it is
|
||
used, the values will not be cached, unless your continuation does some
|
||
explicit caching. <em>Again, we are favoring clarity over micro optimisation</em>.</p>
|
||
|
||
<h4 id="toc_11">SSHKit</h4>
|
||
|
||
<p>Many of the new features in Capistrano whch relate to logging, formatting,
|
||
SSH, connection management and pooling, parallism, batch execution and more
|
||
are from a library that fell out of the Capistrano v3 development process.</p>
|
||
|
||
<p>[*SSHKit*][] is a lower level toolkit, a level higher than <em>Net::SSH</em>, still
|
||
but lacking the roles, environments, rollbacks and other higher level features
|
||
from Capistrano.</p>
|
||
|
||
<p>SSHkit is ideal for use if you need to just connect to a machine and run some
|
||
arbitrary command, for example:</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'># Rakefile (even without Capistrano loaded)
|
||
require 'sshkit'
|
||
desc "Check the uptime of example.com"
|
||
task :uptime do |h|
|
||
execute :uptime
|
||
end</code></pre>
|
||
</div>
|
||
|
||
<p>There is much more than can be done with SSHKit, and we have quite an
|
||
extensive <a href="https://github.com/leehambley/sshkit/blob/master/EXAMPLES.md">list of
|
||
examples</a>. For
|
||
the most part with Capistrano v3, anything that happens inside of an <code>on()</code>
|
||
block is happening in SSHkit, and the documentation from that library is the
|
||
place to go to find more information.</p>
|
||
|
||
<h4 id="toc_12">Command Mapping</h4>
|
||
|
||
<p>This is another feature from SSHKit, designed to remove a little ambiguity
|
||
from preceedings, there is a so-called command map for commands.</p>
|
||
|
||
<p>When executing something like:</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'># Capistrano 2.0.x
|
||
execute "git clone ........ ......."</code></pre>
|
||
</div>
|
||
|
||
<p>The command is passed through to the remote server <em>completely unchanged</em>.
|
||
This includes the options which might be set, such as user, directory, and
|
||
environmental variables. <strong>This is by design.</strong> This feature is designed to
|
||
allow people to write non-trivial commands in
|
||
<a href="https://en.wikipedia.org/wiki/Here_document">heredocs</a> when the need arises,
|
||
for example:</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'># Capistrano 3.0.x
|
||
execute <<-EOBLOCK
|
||
# All of this block is interpreted as Bash script
|
||
if ! [ -e /tmp/somefile ]; then
|
||
touch /tmp/somefile
|
||
end
|
||
EOBLOCK</code></pre>
|
||
</div>
|
||
|
||
<p>The idiomatic way to write that command in Capistrano v3 is to use the
|
||
separated variadaric method to specify the command:</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'># Capistrano 3.0.x
|
||
execute :git, :clone, "........", "......."</code></pre>
|
||
</div>
|
||
|
||
<p>... or for the larger example</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'># Capistrano 3.0.x
|
||
file = '/tmp/somefile'
|
||
unless test("-e #{file}")
|
||
execute :touch, file
|
||
end</code></pre>
|
||
</div>
|
||
|
||
<p>In this way the <em>command map</em> is consulted, the command map maps all unknown
|
||
commands (which in this case is <code>git</code>, the rest of the line are <em>arguments</em> to
|
||
<code>git</code> ) are mapped to <code>/usr/bin/env ...</code>. Meaning that this command would be
|
||
expanded to <code>/usr/bin/env git clone ...... ......</code> which is what happens when
|
||
<code>git</code> is called without a full path, the <code>env</code> program is consulted (perhaps
|
||
indirectly) to determine which <code>git</code> to run.</p>
|
||
|
||
<p>Commands such as <code>rake</code> and <code>rails</code> are often better prefixed by <code>bundle
|
||
exec</code>, and in this case could be mapped to:</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'>SSHKit.config.command_map[:rake] = "bundle exec rake"
|
||
SSHKit.config.command_map[:rails] = "bundle exec rails"</code></pre>
|
||
</div>
|
||
|
||
<p>There can also be a lamda or Proc applied in place of the mapping like so:</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'>SSHKit.config.command_map = Hash.new do |hash, key|
|
||
if %i{rails rake bundle clockwork heroku}.include?(key.to_sym)
|
||
hash[key] = "/usr/bin/env bundle exec #{key}"
|
||
else
|
||
hash[key] = "/usr/bin/env #{key}"
|
||
end
|
||
end</code></pre>
|
||
</div>
|
||
|
||
<p>Between these two options there should be quite powerful options to map
|
||
commands in your environment without having to override internal tasks from
|
||
Capistrano just because a path is different, or a binary has a different name.</p>
|
||
|
||
<p>This can also be <em>slightly</em> abused in environments where <em>shim</em> executables
|
||
are used, for example <code>rbenv</code> <em>wrappers</em>:</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'>SSHKit.config.command_map = Hash.new do |hash, key|
|
||
if %i{rails rake bundle clockwork heroku}.include?(key.to_sym)
|
||
hash[key] = "/usr/bin/env myproject_bundle exec myproject_#{key}"
|
||
else
|
||
hash[key] = "/usr/bin/env #{key}"
|
||
end
|
||
end</code></pre>
|
||
</div>
|
||
|
||
<p>The above assumes that you have done something like <code>rbenv wrapper default
|
||
myproject</code> which creates wrapper binaries which correctly set up the Ruby
|
||
environment without requiring an interactive login shell.</p>
|
||
|
||
<h4 id="toc_13">Testing</h4>
|
||
|
||
<p>The old test suite for Capistrano was purely unit tests, and didn't cover a
|
||
wide varity of problem cases, specifically nothing in the <code>deploy.rb</code> (that is
|
||
the actual <em>deployment</em> code) was tested at all; because of having our own DSL
|
||
implementation, and other slightly odd design points, it was painful to test
|
||
the actual <em>recipes</em>.</p>
|
||
|
||
<p>Testing has been a focus of Capistrano v3. The <em>integration</em> test suite uses
|
||
Vagrant to boot a machine, configures certain scenarios using portable shell
|
||
script, and then executes commands against them, deploying common
|
||
configurations to typical Linux systems. This is slow to execute, but offers
|
||
stronger guarantees that nothing is broken that we've ever been able to give
|
||
before.</p>
|
||
|
||
<p>Capistrano v3 also offers a possibility to swap out backend implementations.
|
||
This is interesting because for the purpose of testing your <em>own</em> recipes you
|
||
can use a <em>printer</em> backend, and verify that the output matched what you
|
||
expected, or use a stubbed backend upon which you can verify that calls were
|
||
made, or not made as expected.</p>
|
||
|
||
<h4 id="toc_14">Arbitrary Logging</h4>
|
||
|
||
<p>Capistrano exposes the methods <code>debug()</code>, <code>info()</code>, <code>warn()</code>, <code>error()</code> and
|
||
<code>fatal()</code> inside of <code>on()</code> blocks which can be used to log using the existing
|
||
logging infrastructure and streaming IO formatters:</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'># Capistrano 3.0.x
|
||
on hosts do |host|
|
||
f = '/some/file'
|
||
if test("[ -d #{f} ]")
|
||
execute :touch, f
|
||
else
|
||
info "#{f} already exists on #{host}!"
|
||
end
|
||
end</code></pre>
|
||
</div>
|
||
|
||
<h3 id="toc_15"> Upgrading</h3>
|
||
|
||
<p>The best place to go here is the <a href="/documentation/upgrading/">upgrading documentation</a> to get deeper
|
||
into the specifics.</p>
|
||
|
||
<p>The simple version is to say that there is <em>no **direct</em>* upgrade path*,
|
||
versions two and three are incompatible.</p>
|
||
|
||
<p>This is partly by design, the old DSL was imprecise in places that would have
|
||
made doing the right thing in most cases tricky, we opted to invest in more
|
||
features and better reliability than investing in keeping a backwards
|
||
compatible API.</p>
|
||
|
||
<p>There are a number of <em>gotchas</em> listed below, but the main points are the new
|
||
names of the built-in roles, as well as that by default Capistrano v3 is
|
||
platform agnostic, if you need Rails support, for migrations, asset pipeline
|
||
and such like, then it's required to <code>require</code> the support files.</p>
|
||
|
||
<h3 id="toc_16">Gotchas</h3>
|
||
|
||
<h4 id="toc_17">Rake DSL Is Additive</h4>
|
||
|
||
<p>In Capistrano v2 if you re-define a task then it replaces the original
|
||
implemetation, this has been used by people to replace internal tasks
|
||
piecemeal with their own implementations</p>
|
||
|
||
<h4 id="toc_18"><code>sudo</code> Behaviour</h4>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!--<div class="container"> -->
|
||
<!-- <h1 class="title"><a href="/">Capistrano</a></h1>-->
|
||
<!-- <a class="extra" href="/">home</a> -->
|
||
<!-- </div> -->
|
||
|
||
<!-- <p class="meta">01 Jun 2013</p>
|
||
|
||
<div id="post">
|
||
<p>After what seems like years of work, the Capistrano team (that's Tom and I)
|
||
are pleased to announce the first <em>major</em> release of Capistrano in almost 5
|
||
years.</p>
|
||
|
||
<p>The reasons behind the length of time between the last architectural overhaul
|
||
and this one are numerous, but it can be summrised to say that Capistrano is a
|
||
widely used tool, and when working around software deployment it's really a
|
||
question of downtime. If we had changed something significant in Capistrano we
|
||
could have taken a lot of sites offline, and made a lot of people very
|
||
unhappy. Until this point we haven't felt that the time has been ripe where
|
||
the benefits of a slightly rocky upgrade path are worth the risks of downtime.</p>
|
||
|
||
<p>It also hasn't helped historically that we've only just gotten to grips with
|
||
Ruby 1.9, and that Bundler's near ubuquity means that now it's trivial to lock
|
||
a Gem at a specific version. With other tools in the Ruby eco system it's
|
||
become easier for us to make significant changes to a tool upon which many
|
||
hundreds of thousands of people rely.</p>
|
||
|
||
<h3 id="toc_0">Design Goals</h3>
|
||
|
||
<p>We had a few goals for this release, in no particular order they were:</p>
|
||
|
||
<ul>
|
||
<li><strong>Get away from our own DSL solution.</strong> Great DSL alternatives (Rake, Sake, Thor,
|
||
etc) are already widely used.</li>
|
||
<li><strong>Better modularisation.</strong> to enable people outside the Rails community to
|
||
benefit from Capistrano's <em>best-practice</em> workflow, and to enable people in
|
||
the Rails community to pick and choose support for components they use
|
||
(Database Migrations, Asset Pipeline, etc)</li>
|
||
<li><strong>Easier Debugging.</strong> A lot of problems with Capistrano come from weirdness
|
||
surrounding environmental issues around PTY vs non-TTY environments, login
|
||
and non-login shells not to mention <em>environment managers</em> such as rvm,
|
||
rbenv and nvm.</li>
|
||
<li><strong>Speed.</strong> We know that in a lot of environments speed of deployment is a
|
||
huge factor, since Rails introduced the <em>Asset Pipeline</em> it's not uncommon
|
||
for a deploy that formerly took 5 seconds now takes 5 minutes. This really
|
||
is mostly out of our control, but with improved support for parallelism,
|
||
rolling restarts we feel confident that things will be quicker and easier to
|
||
keep running quickly now.</li>
|
||
<li><strong>Applicability.</strong> We've always maintained that Capistrano is a terrible
|
||
tool for system provisioning, and that more often than not servers are
|
||
better being setup with Chef, Puppet or similar, whilst we still agree with
|
||
that, the new features in Capistrano really lend themselves to integrating
|
||
with these kinds of tools.</li>
|
||
</ul>
|
||
|
||
<h3 id="toc_1">What's missing?</h3>
|
||
|
||
<p>Before we get too carried away it's worth shortlisting the things that don't
|
||
exist in version three, <strong><em>yet</em></strong>.</p>
|
||
|
||
<ul>
|
||
<li><strong>SSH Gateway Support</strong> SSH Gateway support hasn't been implemented in
|
||
version three yet, I hope that this will be done soon. As I have no direct
|
||
need for it, I haven't the means to test it with a view to implementing it,
|
||
yet.</li>
|
||
<li><strong>Mecurial, Subversion, and CVS Support</strong> These have been removed as we've
|
||
been able to implement the Git SCM in an incredibly neat way that isn't
|
||
compatible with the others. We wanted to break the cycle of always sticking
|
||
with the lowest common denominator, so we are <strong>actively</strong> looking for
|
||
people who are interested in contributing, or sharing expertise on the
|
||
<em>best-practice</em> way of speedily deploying from your respective choice of
|
||
source control.</li>
|
||
<li>**<code>HOSTFILTER</code> ,<code>ROLEFILTER</code> and friends** These have gone away because we
|
||
always felt they were indemic of a bad design desision about using
|
||
Environmental Variables. These will be coming back as flags passed to <code>cap</code>
|
||
on the CLI, and options that can be set on the Capistrano::Application Ruby
|
||
class.</li>
|
||
<li><strong>Shell</strong> The shell has been removed temporarily pending a neater
|
||
implementation, we've got something that we are playing with internally, but
|
||
it needs better readline support, and some more controls around what to do
|
||
when things go badly on some servers, but not others.</li>
|
||
<li><strong>Cold Deploy</strong> The <code>cap deploy:cold</code> is a really old legacy component,
|
||
orignally from the days of the <code>script/spinner</code> where deploying cold
|
||
(starting workers that weren't running), and deploying a <em>warm</em> system were
|
||
different (restarting existing worker pools, which wasn't fun!) By and large
|
||
these things have gone away, and it's time <code>deploy:cold</code> went away. It's
|
||
safe in every case we could find to call setup, and seed and other Rake
|
||
tasks without things blowing up, and that should be the approach we take.
|
||
Tasks on the server should be idempotent, and if something is called twice,
|
||
let it be.</li>
|
||
</ul>
|
||
|
||
<h3 id="toc_2">What's new?</h3>
|
||
|
||
<p>Each section here really deserves it's own sub-heading as some of the new
|
||
features are awesome.</p>
|
||
|
||
<h4 id="toc_3">Rake Integration</h4>
|
||
|
||
<p>We have moved away from our own DSL implemenation to implement Capistrano as a
|
||
<em>Rake</em> application.</p>
|
||
|
||
<p>Rake has always supported being sub-classed, so to speak as a
|
||
<em>sub-application</em>; it is however poorly documented. By subclassing
|
||
<code>Rake::Application</code> one can specify what the <em>Rakefile</em> should look like, where
|
||
to search for it, and how to load other <em>Rakefiles</em>.</p>
|
||
|
||
<p>The <em>Rake</em> DSL is widely used, well known and very powerful. As Rake is
|
||
essentially a dependency resolution system, it offers a lot of nice ways to,
|
||
for example build a tarball as a dependency of uploading it and deploying it.</p>
|
||
|
||
<p>This has allowed us to do away with the <em>copy</em> strategy all together, as it
|
||
can now be implemented from scratch in fewer than ten lines of code. You can
|
||
check out the <a href="/screencasts/replicating_the_copy_strategy">replicating the copy
|
||
strategy</a> screencast and
|
||
acompanying documentation if you want to explore that any further.</p>
|
||
|
||
<p>The guiding principle is dependency resolution, and interoperability with
|
||
other tools, for example:</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'># Capistrano 3.0.x
|
||
task :notify do
|
||
this_release_tag = sh("git describe --abbrev=0 --tags")
|
||
last_ten_commits = sh("git log #{this_release_tag}~10..#{this_release_tag}")
|
||
Mail.deliver do
|
||
to "team@example.com"
|
||
subject "Releasing #{this_release_tag} Now!"
|
||
body last_ten_commits
|
||
end
|
||
end
|
||
|
||
namespace :deploy
|
||
task default: :notify
|
||
end</code></pre>
|
||
</div>
|
||
|
||
<p>The last three lines rely on Rake's additive task declaration, by redefining the
|
||
<code>deploy:default</code> task by adding another dependency. Rake will automatically
|
||
resolve this dependency at Runtime, mailing the recent changelog to your team,
|
||
assuming everything is setup correctly.</p>
|
||
|
||
<h4 id="toc_4">Built In Stage Support</h4>
|
||
|
||
<p>In former versions of Capistrano <em>stage</em> support was an after thought,
|
||
provided through the <code>capistrano-ext</code> Gem, and latterly merged into the main
|
||
codebase, people insisted in still using the <code>capistrano-ext</code> version
|
||
regardless.</p>
|
||
|
||
<p>In Capistrano 3.0.x there's stage support built in, at installation time, two
|
||
stages will be created by default, <em>staging</em> and <em>production</em>; it's easy to
|
||
add more, just add a file to <code>config/deploy/______.rb</code> which follows the
|
||
conventions established in the examples we created for you.</p>
|
||
|
||
<p>To create different stages at installation time, simply set the <code>STAGES</code>
|
||
environmental variable to a comma separated list of stages:</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-bash'>$ cap install STAGES=staging,production,ci,qa</code></pre>
|
||
</div>
|
||
|
||
<h4 id="toc_5">Parallelism</h4>
|
||
|
||
<p>In former versions of Capistrano there was a <em>parallel</em> option to run
|
||
different tasks differently on groups of servers, it looked something like
|
||
this:</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'># Capistrano 2.0.x
|
||
task :restart do
|
||
parallel do |session|
|
||
session.when "in?(:app)", "/u/apps/social/script/restart-mongrel"
|
||
session.when "in?(:web)", "/u/apps/social/script/restart-apache"
|
||
session.else "echo nothing to do"
|
||
end
|
||
end</code></pre>
|
||
</div>
|
||
|
||
<p>This always felt a little unclean, and indeed it's a hack that was originally
|
||
implemeted to facilitate rolling deployments at a large German firm by a
|
||
couple of freelancers who were consulting with them. (Hint, one of those guys
|
||
went on to found Travis-CI!)</p>
|
||
|
||
<p>The equivilent code in under Capistrano v3 would look like this:</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'># Capistrano 3.0.x
|
||
task :restart do
|
||
on :all, in: :parallel do |host|
|
||
if host.roles.include?(:app)
|
||
execute "/u/apps/social/script/restart-mongrel"
|
||
elsif host.roles.include?(:web)
|
||
execute "/u/apps/social/script/restart-web"
|
||
else
|
||
info sprintf("Nothing to do for %s with roles %s", host,
|
||
host.properties.roles)
|
||
end
|
||
end
|
||
end</code></pre>
|
||
</div>
|
||
|
||
<p>The second block of code, that representing the new Rake derived DSL and
|
||
demonstrating how to use the parallel execution mode is a little longer, but I
|
||
think it's clearer, more idiomatic Ruby code which relies less on an intimate
|
||
knowledge of how the Capistrano DSL happens to work. It also hints at the
|
||
built-in logging subsystem, keep reading to learn more.</p>
|
||
|
||
<p>Other modes for parallelism include:</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'># Capistrano 3.0.x
|
||
on :all, in: :groups, max: 3, wait: 5 do
|
||
# Take all servers, in groups of three which execute in parallel
|
||
# wait five seconds between groups of servers.
|
||
# This is perfect for rolling restarts
|
||
end
|
||
|
||
on :all, in: :sequence, wait: 15 do
|
||
# This takes all servers, in sequence and waits 15 seconds between
|
||
# each server, this might be perfect if you are afraid about
|
||
# overloading a shared resource, or want to defer the asset compilation
|
||
# over your cluster owing to worries about load
|
||
end
|
||
|
||
on :all, in: :parallel do
|
||
# This will simply try and execute the commands contained within
|
||
# the block in parallel on all servers. This might be perfect for kicking
|
||
# off something like a Git checkout or similar.
|
||
end</code></pre>
|
||
</div>
|
||
|
||
<p>The internal tasks, for standard deploy recipes make use of all of these as is
|
||
appropriate for the normal case, no need to be afraid of scary slow deploys
|
||
again!</p>
|
||
|
||
<h4 id="toc_6">Streaming IO</h4>
|
||
|
||
<p>This IO streaming model means that results from commands, the commands
|
||
themselves and any other arbitrary output are sent as objects to a class with
|
||
an <code>IO</code>ish interface, the class knows what to do with these things. There's a
|
||
<em>progress</em> formatter which prints dots for each command that is called, as
|
||
well as a <em>pretty</em> formatter which prints the full command, it's output on
|
||
standard out and standard error, as well as the final return status. It would
|
||
be trivial to implement HTML formatters, or formatters that reported to your
|
||
IRC room, or to email. I look forward to seeing more of these cropping up in
|
||
the community.</p>
|
||
|
||
<h4 id="toc_7">Host Definition Access</h4>
|
||
|
||
<p>If you didn't skim over the <em>Parallism</em> section above, you might have noticed we
|
||
did something clever that wasn't possible in Capistrano v2; we accessed the
|
||
<code>host</code> inside the execution block.</p>
|
||
|
||
<p>For a lot of reasons in Capistrano v2 is wasn't possible to do this, the block
|
||
was essentially evaluated once and called verbatim on each host. This lead to
|
||
disappointing missing features such as not being able to pull the host list
|
||
out of Capistrano and examine the roles to do something like controlling chef
|
||
solo, or similar.</p>
|
||
|
||
<p>In Capistrano v3 the <code>host</code> object is the same object that is created when a
|
||
server is defined, and is internally used, for example to pass to an ERB
|
||
template for rendering a last-deploy message that is dumped onto each server
|
||
after a successful deployment. The last deploy log includes everything
|
||
Capistrano knew about that server during the deployment.</p>
|
||
|
||
<blockquote>
|
||
<p>Users of Capistrano v2 may be familiar with the perenial <code>cap deploy:cleanup</code>
|
||
problem which came to light when servers differed in their old releases list,
|
||
imagine a scenario with two servers, one has been your bread-and-butter since
|
||
you launched, it has hundreds of old releases from all your wonderful deploys
|
||
over the months or years. The second server has been in the cluster for about
|
||
a month, it didn't quite slot-in cleanly, so the list of old releases looks a
|
||
bit weird, you deleted a few by hand, and anyway there might only be ten-or-so
|
||
releases there.</p>
|
||
|
||
<p>Now imagine that you call <code>cap deploy:cleanup</code>, old <code>capture()</code>
|
||
implementations silently only ran on the first server that matched the
|
||
properties defined, so server one returned a list of ~95 old timestamped
|
||
release directories. Next Capistrano v2 would call <code>rm -rf
|
||
release1..release95</code> on <strong>both</strong> servers, causing server two to err our, and
|
||
leaving an undefined state on server one, as Capistrano would simply hang up
|
||
both connections.</p>
|
||
</blockquote>
|
||
|
||
<p>This cleanup routine can now be better implemented as follows (which is
|
||
actually more or less the actual implementation in the the new Gem):</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'># Capistrano 3.0.x
|
||
desc "Cleanup all old releases (keeps #{fetch(:releases_to_keeo_on_cleanup)}
|
||
old releases"
|
||
task :cleanup do
|
||
keep_releases = fetch(:releases_to_keep_on_cleanup)
|
||
releases = capture(:ls, fetch(:releases_directory))
|
||
release_to_delete = releases.sort_by { |r| rn.to_i }.slice(1..-(keep_releases + 1))
|
||
releases_to_delete.each do |r|
|
||
execute :rm, fetch(:releases_directory).join(r)
|
||
end
|
||
end</code></pre>
|
||
</div>
|
||
|
||
<p>Some handy things to note here are that both server one and server two in our
|
||
contrived example will both evaluate that independently, and when both servers
|
||
are finished removing old releases the <code>task :cleanup</code> block will have
|
||
finished.</p>
|
||
|
||
<p>Also in Capistrano v3 most path varaibles are [<code>Pathname</code>] objects, so they natively
|
||
respond to things like <code>#basename</code>, <code>#expand_path</code>, <code>#join</code> and similar.</p>
|
||
|
||
<p><strong>Warning:</strong> <code>#expand_path</code> probably won't do what you expect, it will execute
|
||
on your <em>workstation</em> machine, and not on the remote host, so it's possible
|
||
that it will return an error in the case of paths which exist remotely but not
|
||
locally.</p>
|
||
|
||
<h4 id="toc_8">Host Properties</h4>
|
||
|
||
<p>As the <code>host</code> object is now available to the task blocks, it made sense to make
|
||
it possible to store arbitrarty values against them.</p>
|
||
|
||
<p>Enter <code>host.properties</code>. This is a simple
|
||
<a href="http://www.ruby-doc.org/stdlib-2.0/libdoc/ostruct/rdoc/OpenStruct.html"><em>OpenStruct</em></a>
|
||
which can be used to store any additional properties which are important for
|
||
your application.</p>
|
||
|
||
<p>An example of it's usage might be:</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'>h = SSHKit::Host.new 'example.com'
|
||
h.properties.roles ||= %i{wep app}</code></pre>
|
||
</div>
|
||
|
||
<h4 id="toc_9">More Expressive Command Language</h4>
|
||
|
||
<p>In Capistrano v2, it wasn't uncommon to find commands such as:</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'># Capistrano 2.0.x
|
||
task :precompile, :roles => lambda { assets_role }, :except => { :no_release => true } do
|
||
run <<-CMD.compact
|
||
cd -- #{latest_release} &&
|
||
RAILS_ENV=#{rails_env.to_s.shellescape} #{asset_env} #{rake} assets:precompile
|
||
CMD
|
||
end</code></pre>
|
||
</div>
|
||
|
||
<p>In Capistrano v3 this looks more like this:</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'># Capistrano 3.0.x
|
||
task :precompile do
|
||
on :sprockets_asset_host, reject: lambda { |h| h.properties.no_release } do
|
||
within fetch(:latest_release_directory)
|
||
with rails_env: fetch(:rails_env) do
|
||
execute :rake, 'assets:precompile'
|
||
end
|
||
end
|
||
end
|
||
end</code></pre>
|
||
</div>
|
||
|
||
<p>Again, with other examples this format is a little longer, but much more
|
||
expressive, and all the nightmare of shell escaping is handled interally for
|
||
you, environmental variables are capitalised and applied at the correct point
|
||
(i.e between the <code>cd</code> and <code>rake</code> calls in this case).</p>
|
||
|
||
<p>Other options here include <code>as :a_user</code> and</p>
|
||
|
||
<h4 id="toc_10">Better <em>magic</em> Variable Support</h4>
|
||
|
||
<p>In Capistrano v2 there were certain bits of magic where if calling a variable
|
||
and NoMethodError would have been raised (for example the
|
||
<code>latest_release_directory</code> varaible). This variable never existed on the
|
||
global namespace, as a fall-back the list of <code>set()</code> variables would be
|
||
consulted.</p>
|
||
|
||
<p>This magic lead to times when people were not recognising that magic variables
|
||
were even being used. The magic variable system of Capistrano v2 did also
|
||
include a way to <code>fetch(:some_variable, 'with a default value')</code> incase the
|
||
variable might not be set already, but it wasn't widely used, and more often
|
||
than not people just used things like <code>latest_release_directory</code> never knowing
|
||
that behind the scenes an exception was raised, then rescued, and that
|
||
<code>:latest_release_directory</code> in the variable map was actually a continuation
|
||
that was evaluated the first time it was used, and the value then cached until
|
||
the end of the script.</p>
|
||
|
||
<p>The system has now 100% less magic. If you set a variable using <code>set()</code>, it
|
||
can be fetched with <code>fetch()</code>, if the value you set into the variable responds
|
||
to <code>#call</code> then it will be executed in the current context whenever it is
|
||
used, the values will not be cached, unless your continuation does some
|
||
explicit caching. <em>Again, we are favoring clarity over micro optimisation</em>.</p>
|
||
|
||
<h4 id="toc_11">SSHKit</h4>
|
||
|
||
<p>Many of the new features in Capistrano whch relate to logging, formatting,
|
||
SSH, connection management and pooling, parallism, batch execution and more
|
||
are from a library that fell out of the Capistrano v3 development process.</p>
|
||
|
||
<p>[*SSHKit*][] is a lower level toolkit, a level higher than <em>Net::SSH</em>, still
|
||
but lacking the roles, environments, rollbacks and other higher level features
|
||
from Capistrano.</p>
|
||
|
||
<p>SSHkit is ideal for use if you need to just connect to a machine and run some
|
||
arbitrary command, for example:</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'># Rakefile (even without Capistrano loaded)
|
||
require 'sshkit'
|
||
desc "Check the uptime of example.com"
|
||
task :uptime do |h|
|
||
execute :uptime
|
||
end</code></pre>
|
||
</div>
|
||
|
||
<p>There is much more than can be done with SSHKit, and we have quite an
|
||
extensive <a href="https://github.com/leehambley/sshkit/blob/master/EXAMPLES.md">list of
|
||
examples</a>. For
|
||
the most part with Capistrano v3, anything that happens inside of an <code>on()</code>
|
||
block is happening in SSHkit, and the documentation from that library is the
|
||
place to go to find more information.</p>
|
||
|
||
<h4 id="toc_12">Command Mapping</h4>
|
||
|
||
<p>This is another feature from SSHKit, designed to remove a little ambiguity
|
||
from preceedings, there is a so-called command map for commands.</p>
|
||
|
||
<p>When executing something like:</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'># Capistrano 2.0.x
|
||
execute "git clone ........ ......."</code></pre>
|
||
</div>
|
||
|
||
<p>The command is passed through to the remote server <em>completely unchanged</em>.
|
||
This includes the options which might be set, such as user, directory, and
|
||
environmental variables. <strong>This is by design.</strong> This feature is designed to
|
||
allow people to write non-trivial commands in
|
||
<a href="https://en.wikipedia.org/wiki/Here_document">heredocs</a> when the need arises,
|
||
for example:</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'># Capistrano 3.0.x
|
||
execute <<-EOBLOCK
|
||
# All of this block is interpreted as Bash script
|
||
if ! [ -e /tmp/somefile ]; then
|
||
touch /tmp/somefile
|
||
end
|
||
EOBLOCK</code></pre>
|
||
</div>
|
||
|
||
<p>The idiomatic way to write that command in Capistrano v3 is to use the
|
||
separated variadaric method to specify the command:</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'># Capistrano 3.0.x
|
||
execute :git, :clone, "........", "......."</code></pre>
|
||
</div>
|
||
|
||
<p>... or for the larger example</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'># Capistrano 3.0.x
|
||
file = '/tmp/somefile'
|
||
unless test("-e #{file}")
|
||
execute :touch, file
|
||
end</code></pre>
|
||
</div>
|
||
|
||
<p>In this way the <em>command map</em> is consulted, the command map maps all unknown
|
||
commands (which in this case is <code>git</code>, the rest of the line are <em>arguments</em> to
|
||
<code>git</code> ) are mapped to <code>/usr/bin/env ...</code>. Meaning that this command would be
|
||
expanded to <code>/usr/bin/env git clone ...... ......</code> which is what happens when
|
||
<code>git</code> is called without a full path, the <code>env</code> program is consulted (perhaps
|
||
indirectly) to determine which <code>git</code> to run.</p>
|
||
|
||
<p>Commands such as <code>rake</code> and <code>rails</code> are often better prefixed by <code>bundle
|
||
exec</code>, and in this case could be mapped to:</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'>SSHKit.config.command_map[:rake] = "bundle exec rake"
|
||
SSHKit.config.command_map[:rails] = "bundle exec rails"</code></pre>
|
||
</div>
|
||
|
||
<p>There can also be a lamda or Proc applied in place of the mapping like so:</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'>SSHKit.config.command_map = Hash.new do |hash, key|
|
||
if %i{rails rake bundle clockwork heroku}.include?(key.to_sym)
|
||
hash[key] = "/usr/bin/env bundle exec #{key}"
|
||
else
|
||
hash[key] = "/usr/bin/env #{key}"
|
||
end
|
||
end</code></pre>
|
||
</div>
|
||
|
||
<p>Between these two options there should be quite powerful options to map
|
||
commands in your environment without having to override internal tasks from
|
||
Capistrano just because a path is different, or a binary has a different name.</p>
|
||
|
||
<p>This can also be <em>slightly</em> abused in environments where <em>shim</em> executables
|
||
are used, for example <code>rbenv</code> <em>wrappers</em>:</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'>SSHKit.config.command_map = Hash.new do |hash, key|
|
||
if %i{rails rake bundle clockwork heroku}.include?(key.to_sym)
|
||
hash[key] = "/usr/bin/env myproject_bundle exec myproject_#{key}"
|
||
else
|
||
hash[key] = "/usr/bin/env #{key}"
|
||
end
|
||
end</code></pre>
|
||
</div>
|
||
|
||
<p>The above assumes that you have done something like <code>rbenv wrapper default
|
||
myproject</code> which creates wrapper binaries which correctly set up the Ruby
|
||
environment without requiring an interactive login shell.</p>
|
||
|
||
<h4 id="toc_13">Testing</h4>
|
||
|
||
<p>The old test suite for Capistrano was purely unit tests, and didn't cover a
|
||
wide varity of problem cases, specifically nothing in the <code>deploy.rb</code> (that is
|
||
the actual <em>deployment</em> code) was tested at all; because of having our own DSL
|
||
implementation, and other slightly odd design points, it was painful to test
|
||
the actual <em>recipes</em>.</p>
|
||
|
||
<p>Testing has been a focus of Capistrano v3. The <em>integration</em> test suite uses
|
||
Vagrant to boot a machine, configures certain scenarios using portable shell
|
||
script, and then executes commands against them, deploying common
|
||
configurations to typical Linux systems. This is slow to execute, but offers
|
||
stronger guarantees that nothing is broken that we've ever been able to give
|
||
before.</p>
|
||
|
||
<p>Capistrano v3 also offers a possibility to swap out backend implementations.
|
||
This is interesting because for the purpose of testing your <em>own</em> recipes you
|
||
can use a <em>printer</em> backend, and verify that the output matched what you
|
||
expected, or use a stubbed backend upon which you can verify that calls were
|
||
made, or not made as expected.</p>
|
||
|
||
<h4 id="toc_14">Arbitrary Logging</h4>
|
||
|
||
<p>Capistrano exposes the methods <code>debug()</code>, <code>info()</code>, <code>warn()</code>, <code>error()</code> and
|
||
<code>fatal()</code> inside of <code>on()</code> blocks which can be used to log using the existing
|
||
logging infrastructure and streaming IO formatters:</p>
|
||
|
||
<div>
|
||
<pre data-line=''><code class='language-ruby'># Capistrano 3.0.x
|
||
on hosts do |host|
|
||
f = '/some/file'
|
||
if test("[ -d #{f} ]")
|
||
execute :touch, f
|
||
else
|
||
info "#{f} already exists on #{host}!"
|
||
end
|
||
end</code></pre>
|
||
</div>
|
||
|
||
<h3 id="toc_15"> Upgrading</h3>
|
||
|
||
<p>The best place to go here is the <a href="/documentation/upgrading/">upgrading documentation</a> to get deeper
|
||
into the specifics.</p>
|
||
|
||
<p>The simple version is to say that there is <em>no **direct</em>* upgrade path*,
|
||
versions two and three are incompatible.</p>
|
||
|
||
<p>This is partly by design, the old DSL was imprecise in places that would have
|
||
made doing the right thing in most cases tricky, we opted to invest in more
|
||
features and better reliability than investing in keeping a backwards
|
||
compatible API.</p>
|
||
|
||
<p>There are a number of <em>gotchas</em> listed below, but the main points are the new
|
||
names of the built-in roles, as well as that by default Capistrano v3 is
|
||
platform agnostic, if you need Rails support, for migrations, asset pipeline
|
||
and such like, then it's required to <code>require</code> the support files.</p>
|
||
|
||
<h3 id="toc_16">Gotchas</h3>
|
||
|
||
<h4 id="toc_17">Rake DSL Is Additive</h4>
|
||
|
||
<p>In Capistrano v2 if you re-define a task then it replaces the original
|
||
implemetation, this has been used by people to replace internal tasks
|
||
piecemeal with their own implementations</p>
|
||
|
||
<h4 id="toc_18"><code>sudo</code> Behaviour</h4>
|
||
|
||
</div>
|
||
-->
|
||
|
||
<!--</div> [> /container <] -->
|
||
|
||
<footer>
|
||
<div class="row">
|
||
<div class="large-4 columns">
|
||
<ul>
|
||
<li><a href="/about">About Capistrano</a></li>
|
||
<li><a href="https://github.com/capistrano/capistrano/blob/master/CONTRIBUTING">Contributing</a></li>
|
||
<li><a href="https://rubygems.org/gems/capistrano/versions">Releases</a></li>
|
||
<li><a href="/documentation/upgrading">Upgrading</a></li>
|
||
<li><a href="/security">Security</a></li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="large-4 columns">
|
||
<ul>
|
||
<li><a href="http://stackoverflow.com/questions/tagged/capistrano">StackOverflow</a></li>
|
||
<li><a href="https://groups.google.com/forum/#!forum/capistrano">Mailing List</a></li>
|
||
<li><a href=".... ">Commercial Support</a></li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="large-4 columns">
|
||
<ul class="social icons">
|
||
<li><a href="//twitter.com/capistranorb"><i class="foundicon-twitter"></i></a></li>
|
||
<li><a href="//github.com/capistrano"><i class="foundicon-github"></i></a></li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</footer>
|
||
|
||
|
||
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.min.js"></script>
|
||
<script>
|
||
if (!window.jQuery) {
|
||
document.write('<script src="/js/jquery-1.7.min"><\/script>');
|
||
}
|
||
</script>
|
||
<script src="/js/jquery.githubRepoWidget.min.js"></script>
|
||
<script src="/js/prism.js"></script>
|
||
<script src="/js/prism.ruby.js"></script>
|
||
<a href="https://github.com/capistrano/documentation"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_white_ffffff.png" alt="Fork me on GitHub"></a>
|
||
</body>
|
||
</html>
|