commit 61aec364b2c20b15cf494e19f156fec74910fdce Author: Lee Hambley Date: Wed Jun 12 22:44:00 2013 +0200 Commit this stuff before I lose it all diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..c08f9add --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +_site \ No newline at end of file diff --git a/Gemfile b/Gemfile new file mode 100644 index 00000000..5ebb2f9b --- /dev/null +++ b/Gemfile @@ -0,0 +1,6 @@ +source "https://rubygems.org" + +gem "debugger" +gem "jekyll" +gem "redcarpet" +gem "git" diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 00000000..45024e23 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,50 @@ +GEM + remote: https://rubygems.org/ + specs: + classifier (1.3.3) + fast-stemmer (>= 1.0.0) + colorator (0.1) + columnize (0.3.6) + commander (4.1.3) + highline (~> 1.6.11) + debugger (1.6.0) + columnize (>= 0.3.1) + debugger-linecache (~> 1.2.0) + debugger-ruby_core_source (~> 1.2.1) + debugger-linecache (1.2.0) + debugger-ruby_core_source (1.2.2) + directory_watcher (1.4.1) + fast-stemmer (1.0.2) + git (1.2.5) + highline (1.6.19) + jekyll (1.0.2) + classifier (~> 1.3) + colorator (~> 0.1) + commander (~> 4.1.3) + directory_watcher (~> 1.4.1) + kramdown (~> 1.0.2) + liquid (~> 2.3) + maruku (~> 0.5) + pygments.rb (~> 0.5.0) + safe_yaml (~> 0.7.0) + kramdown (1.0.2) + liquid (2.5.0) + maruku (0.6.1) + syntax (>= 1.0.0) + posix-spawn (0.3.6) + pygments.rb (0.5.0) + posix-spawn (~> 0.3.6) + yajl-ruby (~> 1.1.0) + redcarpet (2.2.2) + safe_yaml (0.7.1) + syntax (1.0.0) + yajl-ruby (1.1.0) + +PLATFORMS + ruby + +DEPENDENCIES + debugger + git + jekyll + redcarpet diff --git a/_config.yml b/_config.yml new file mode 100644 index 00000000..55e2e260 --- /dev/null +++ b/_config.yml @@ -0,0 +1,7 @@ +name: Capistrano +pygments: true +markdown: redcarpet +redcarpet: + extensions: ["no_intra_emphasis", "fenced_code_blocks", "autolink", "tables", "with_toc_data"] +table_of_contents: + dirs: [documentation] diff --git a/_includes/footer.html b/_includes/footer.html new file mode 100644 index 00000000..7124869f --- /dev/null +++ b/_includes/footer.html @@ -0,0 +1,28 @@ +
+
+
+
+ +
+ + + +
+
    +
  • +
+
+
+
+
diff --git a/_includes/header.html b/_includes/header.html new file mode 100644 index 00000000..fce1f88d --- /dev/null +++ b/_includes/header.html @@ -0,0 +1,5 @@ +
+ + + +
diff --git a/_includes/navigation.html b/_includes/navigation.html new file mode 100644 index 00000000..a3b0034b --- /dev/null +++ b/_includes/navigation.html @@ -0,0 +1,31 @@ +
+ +
diff --git a/_layouts/default.html b/_layouts/default.html new file mode 100644 index 00000000..73d188ce --- /dev/null +++ b/_layouts/default.html @@ -0,0 +1,47 @@ + + + + + + + {{ page.title }} + + + + + + + + + + + + + + {% include header.html %} + +
+
+ {% include navigation.html %} +
+
+
+

{{ page.title }}

+ {{ content }} +
+
+
+ + + + + + + + + + + {% include footer.html %} + + + diff --git a/_layouts/post.html b/_layouts/post.html new file mode 100644 index 00000000..83bae3f4 --- /dev/null +++ b/_layouts/post.html @@ -0,0 +1,8 @@ +--- +layout: default +--- +

{{ page.date | date_to_string }}

+ +
+{{ content }} +
diff --git a/_layouts/table_of_contents.html b/_layouts/table_of_contents.html new file mode 100644 index 00000000..692bc9d8 --- /dev/null +++ b/_layouts/table_of_contents.html @@ -0,0 +1 @@ +Why is this here? \ No newline at end of file diff --git a/_plugins/gitactivity.rb b/_plugins/gitactivity.rb new file mode 100644 index 00000000..e22b004b --- /dev/null +++ b/_plugins/gitactivity.rb @@ -0,0 +1,32 @@ +require 'git' + +module Jekyll + class GitActivityTag < Liquid::Tag + + def initialize(tag_name, text, tokens) + super + end + + def render(context) + result = "" + g = Git.open(File.join(Dir.getwd, "..")) + + index = 0 + g.log.each do |log| + if(index < 10) + result << "
  • " + result << log.date.strftime("%d %b") + result << " - " + result << log.message + result << "
  • " + index += 1 + end + end + "" + end + end +end + +Liquid::Template.register_tag('gitactivity', Jekyll::GitActivityTag) \ No newline at end of file diff --git a/_plugins/table_of_contents.rb b/_plugins/table_of_contents.rb new file mode 100644 index 00000000..832935c7 --- /dev/null +++ b/_plugins/table_of_contents.rb @@ -0,0 +1,30 @@ +require 'git' + +module Jekyll + class TableOfContentsTag < Liquid::Tag + def initialize(tag_name, text, tokens) + super + end + def render(context) + result = "" + g = Git.open(File.join(Dir.getwd, "..")) + + index = 0 + g.log.each do |log| + if(index < 10) + result << "
  • " + result << log.date.strftime("%d %b") + result << " - " + result << log.message + result << "
  • " + index += 1 + end + end + "" + end + end +end + +Liquid::Template.register_tag('table_of_contents', Jekyll::TableOfContentsTag) \ No newline at end of file diff --git a/_posts/2013-05-18-welcome-to-jekyll.markdown b/_posts/2013-05-18-welcome-to-jekyll.markdown new file mode 100644 index 00000000..2d544278 --- /dev/null +++ b/_posts/2013-05-18-welcome-to-jekyll.markdown @@ -0,0 +1,24 @@ +--- +layout: post +title: "Welcome to Jekyll!" +date: 2013-05-18 15:09:49 +categories: jekyll update +--- + +You'll find this post in your `_posts` directory - edit this post and re-build (or run with the `-w` switch) to see your changes! +To add new posts, simply add a file in the `_posts` directory that follows the convention: YYYY-MM-DD-name-of-post.ext. + +Jekyll also offers powerful support for code snippets: + +{% highlight ruby %} +def print_hi(name) + puts "Hi, #{name}" +end +print_hi('Tom') +#=> prints 'Hi, Tom' to STDOUT. +{% endhighlight %} + +Check out the [Jekyll docs][jekyll] for more info on how to get the most out of Jekyll. File all bugs/feature requests at [Jekyll's GitHub repo][jekyll-gh]. + +[jekyll-gh]: https://github.com/mojombo/jekyll +[jekyll]: http://jekyllrb.com diff --git a/_posts/2013-06-01-release-announcement.markdown b/_posts/2013-06-01-release-announcement.markdown new file mode 100644 index 00000000..407e2f58 --- /dev/null +++ b/_posts/2013-06-01-release-announcement.markdown @@ -0,0 +1,529 @@ +--- +layout: post +title: "Capistrano Version 3 Release Announcement" +date: 2013-06-01 00:00:00 +--- + +After what seems like years of work, the Capistrano team (that's Tom and I) +are pleased to announce the first *major* release of Capistrano in almost 5 +years. + +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. + +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. + +### Design Goals + +We had a few goals for this release, in no particular order they were: + +* **Get away from our own DSL solution.** Great DSL alternatives (Rake, Sake, Thor, + etc) are already widely used. +* **Better modularisation.** to enable people outside the Rails community to + benefit from Capistrano's *best-practice* workflow, and to enable people in + the Rails community to pick and choose support for components they use + (Database Migrations, Asset Pipeline, etc) +* **Easier Debugging.** 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 *environment managers* such as rvm, + rbenv and nvm. +* **Speed.** We know that in a lot of environments speed of deploment is a + huge factor, since Rails introduced the *Asset Pipeline* 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 are bettwe not. +* **Applicability.** 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. + +### What's missing? + +Before we get too carried away it's worth shortlisting the things that don't +exist in version three, *yet*. + +* *Run Locally.* `run_locally` was always a bit of the poor relation in the + family, it didn't quite work the same way as other things, and I never liked + the idea that you were binding a tool that in principle should be cross + platform (Ruby, and Capistrano) to locally executing shell commands which + probably only work on POSIX-like operating systems. I hope that + `run_locally` will stay gone, and that as a community we can find better + ways of doing things that always relied on locally creating tarballs, or + writing configuration files, or similar. +* *SSH Gateway Support.* 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. +* *Mecurial, Subversion, and CVS Support.* 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 **actively** looking for + people who are interested in contributing, or sharing expertise on the + *best-practice* way of speedily deploying from your respective choice of + source control. +* *`HOSTFILTER` ,`ROLEFILTER` and friends.* These have gone away because I + always felt they were indemic of a bad design desision about using + Environmental Variables. These will be coming back as flags passed to `cap` + on the CLI, and options that can be set on the Capistrano::Application Ruby + class. +* *Shell.* 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. +* *Cold Deploy.* The `cap deploy:cold` is a really old legacy component, + orignally from the days of the `script/spinner` where deploying cold + (starting workers that weren't running), and deploying a *warm* system were + different (restarting existing worker pools, which wasn't fun!) By and large + these things have gone away, and it's time `deploy:cold` 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. +* *Coloured Output.*✝ The capistrano-colours Gem was (fairly) recently merged + into Capistrano v2, this has gone away in favour of a IO streaming model + inspired by MiniTest/Pride. Read more below in new features. + +### What's new? + +Each section here really deserves it's own sub-heading as some of the new +features are awesome. + +#### Rake Integration + +We have moved away from our own DSL implemenation to implement Capistrano as a +Rake application. + +Rake has always supported being sub-classed, so to speak as a +*sub-application*; it is however poorly documented. By subclassing +Rake::Application one can specify what the *Rakefile* should look like, where +to search for it, and how to load other *Rakefiles*. + +The *Rake* 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. + +This has allowed us to do away with the *copy* strategy all together, as it +can now be implemented from scratch in fewer than ten lines of code. You can +check out the [replicating the copy strategy][] screencast and acompanying +documentation if you want to explore that any further. + +The guiding principle is dependency resolution, and interoperability with +other tools, for example: + +{% highlight ruby %} +# No description, this is an internal method +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 +{% endhighlight %} + +The last three lines rely on Rake's additive task declaration, by redefining the +`deploy:default` 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. + +#### Built In Stage Support + +#### Parallelism + +In former versions of Capistrano there was a *parallel* option to run +different tasks differently on groups of servers, it looked something like +this: + +{% highlight ruby %} +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 +{% endhighlight %} + +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!) + +The equivilent code in under Capistrano v3 would look like this: + +{% highlight ruby %} +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 "Nothing to do for #{host} with roles #{host.properties.roles}." + end + end +end +{% endhighlight %} + +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. + +Other modes for parallelism include: + +{% highlight ruby %} +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 +{% endhighlight %} + +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! + +#### Streaming IO + +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 `IO`ish interface, the class knows what to do with these things. There's a +*progress* formatter which prints dots for each command that is called, as +well as a *pretty* 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. + +#### Host Definition Access + +If you didn't skim over the *Parallism* section above, you might have noticed we +did something clever that wasn't possible in Capistrano v2; we accessed the +`host` inside the execution block. + +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. + +In Capistrano v3 the `host` 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. + +> Users of Capistrano v2 may be familiar with the perenial `cap deploy:cleanup` +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. + +> Now imagine that you call `cap deploy:cleanup`, old `capture()` +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 `rm -rf +release1..release95` on **both** servers, causing server two to err our, and +leaving an undefined state on server one, as Capistrano would simply hang up +both connections. + +This cleanup routine can now be better implemented as follows (which is +actually more or less the actual implenetation in the the new Gem): + +{% highlight ruby %} +desc "Cleanup all old releases (keeps #{fetch(:releases_to_keeo_on_cleanup)} +old releases" +task :cleanup do + keep_releases = fetch(:releases_to_keeo_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 +{% endhighlight %} + +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 `task :cleanup` block will have +finished. + +Also in Capistrano v3 most path varaibles are [`Pathname`] objects, so they natively +respond to things like `#basename`, `#expand_path`, `#join` and similar. + +**Warning:** `#expand_path` probably won't do what you expect, it will execute +on your *workstation* 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. + +#### Host Properties + +As the `host` object is now available to the task blocks, it made sense to make +it possible to store arbitrarty values against them. + +Enter `host.properties`. This is a simple [*OpenStruct*][] which can be used to +store any additional properties which are important for your application. + +An example of it's usage might be: + +{% highlight ruby %} +h = SSHKit::Host.new 'example.com' +h.properties.roles ||= %i{wep app} +{% endhighlight %} + +#### More Expressive Command Language + +In Capistrano v2, it wasn't uncommon to find commands such as: + +{% highlight ruby %} +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 +{% endhighlight %} + +In Capistrano v3 this looks more like this: + +{% highlight ruby %} +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 +{% endhighlight %} + +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 `cd` and `rake` calls in this case). + +Other options here include `as :a_user` and + +#### Better *magic* Variable Support + +In Capistrano v2 there were certain bits of magic where if calling a variable +and NoMethodError would have been raised (for example the +`latest_release_directory` varaible). This variable never existed on the +global namespace, as a fall-back the list of `set()` variables would be +consulted. + +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 `fetch(:some_variable, 'with a default value')` incase the +variable might not be set already, but it wasn't widely used, and more often +than not people just used things like `latest_release_directory` never knowing +that behind the scenes an exception was raised, then rescued, and that +`:latest_release_directory` 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. + +The system has now 100% less magic. If you set a variable using `set()`, it +can be fetched with `fetch()`, if the value you set into the variable responds +to `#call` 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. *Again, we are favoring clarity over micro optimisation*. + +#### SSHKit + +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. + +[*SSHKit*][] is a lower level toolkit, a level higher than *Net::SSH*, still +but lacking the roles, environments, rollbacks and other higher level features +from Capistrano. + +SSHkit is ideal for use if you need to just connect to a machine and run some +arbitrary command, for example: + +{% highlight ruby %} +# Rakefile +require 'sshkit' +desc "Check the uptime of example.com" +task :uptime do |h| + execute :uptime +end +{% endhighlight %} + +There is much more than can be done with SSHKit, and we have quite an +extensive [list of examples][]. For the most part with Capistrano v3, anything +that happens inside of an `on()` block is happening in SSHkit, and the +documentation from that library is the place to go to find more information. + +#### Command Mapping + +This is another feature from SSHKit, designed to remove a little ambiguity +from preceedings, there is a so-called command map for commands. + +When executing something like: + +{% highlight ruby %} +execute "git clone ........ ......." +{% endhighlight %} + +The command is passed through to the remote server *completely unchanged*. +This includes the options which might be set, such as user, directory, and +environmental variables. **This is by design.** This feature is designed to +allow people to write non-trivial commands in [heredocs][] when the need +arises. + +The idiomatic way to write that command in Capistrano v3 is to use the +separated variadaric method to specify the command: + +{% highlight ruby %} +execute :git, :clone, "........", "......." +{% endhighlight %} + +In this way the *command map* is consulted, the command map maps all unknown +commands (which in this case is `git`, the rest of the line are *arguments* to +`git` ) are mapped to `/usr/bin/env ...`. Meaning that this command would be +expanded to `/usr/bin/env git clone ...... ......` which is what happens when +`git` is called without a full path, the `env` program is consulted (perhaps +indirectly) to determine which `git` to run. + +Commands such as `rake` and `rails` are often better prefixed by `bundle +exec`, and in this case could be mapped to: + +{% highlight ruby %} +SSHKit.config.command_map[:rake] = "bundle exec rake" +SSHKit.config.command_map[:rails] = "bundle exec rails" +{% endhighlight %} + +There can also be a lamda or Proc applied in place of the mapping like so: + +{% highlight 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 +{% endhighlight %} + +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. + +This can also be *slightly* abused in environments where *shim* executables +are used, for example `rbenv` *wrappers*: + +{% highlight 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 +{% endhighlight %} + +The above assumes that you have done something like `rbenv wrapper default +myproject` which creates wrapper binaries which correctly set up the Ruby +environment without requiring an interactive login shell. + +#### Testing + +The old test suite for Capistrano was purely unit tests, and didn't cover a +wide varity of problem cases, specifically nothing in the `deploy.rb` (that is +the actual *deployment* 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 *recipes*. + +Testing has been a focus of Capistrano v3. The *integration* 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. + +Capistrano v3 also offers a possibility to swap out backend implementations. +This is interesting because for the purpose of testing your *own* recipes you +can use a *printer* 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. + +#### Arbitrary Logging + +Capistrano exposes the methods `debug()`, `info()`, `warn()`, `error()` and +`fatal()` inside of `on()` blocks which can be used to log using the existing +logging infrastructure and streaming IO formatters: + +{% highlight ruby %} +on hosts do |host| + f = '/some/file' + if test("[ -d #{f} ]") + execute :touch, f + else + info "#{f} already exists on #{host}!" + end +end +{% endhighlight %} + +### Upgrading + +The best place to go here is the [upgrading documentation][] to get deeper +into the specifics. + +The simple version is to say that there is *no **direct** upgrade path*, +versions two and three are incompatible. + +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. + +There are a number of *gotchas* 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 `require` the support files. + +### Gotchas + +#### Rake DSL Is Additive + +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 + +#### `sudo` Behaviour diff --git a/changes.html b/changes.html new file mode 100644 index 00000000..6d0d3005 --- /dev/null +++ b/changes.html @@ -0,0 +1,4 @@ +--- +layout: default +title: "Recent Site Changes" +--- \ No newline at end of file diff --git a/css/capistrano.css b/css/capistrano.css new file mode 100644 index 00000000..65f01441 --- /dev/null +++ b/css/capistrano.css @@ -0,0 +1,66 @@ +a { + text-decoration: none; + color: #52C1DB; +} + +body, p, a { + font-family: 'Open-Sans', sans-serif; + -webkit-font-smoothing: antialiased; +} + +p, h1, h2, h3, h4, h5, h6 { + color: #1C1B39; +} + +.header { + height: 195px; + background-color: #1C1B39; + background-image: url('/images/CapistranoGraphicWireframe.png'); + background-repeat: no-repeat; + background-size: 275px; + position: relative; +} + +.header a { + color: #52C1DB; +} + +pre { + margin: 1.3em 0 1em; + padding: 1.3em; +} + +code, kbd, pre, samp { + font-family: 'droid-sans-mono', monospace, serif; + background-color: rgb(250, 250, 250); +} + +.header a.brand img { + margin: 6.5em auto auto 7em; + width: 300px; +} + +h1, h2, h3, h4, h5 h6 { + font-family: 'Enriqueta', serif; +} + +.post-date { + font-weight: bold; +} + +h1 { + font-size: 36pt; +} + +h2 { + font-size: 18pt; +} + +.nav.nav-list { + border-top: none; +} + +.content { + margin: 1.3em 0 1em; + padding: 1.3em; +} diff --git a/css/syntax.css b/css/syntax.css new file mode 100644 index 00000000..2774b764 --- /dev/null +++ b/css/syntax.css @@ -0,0 +1,60 @@ +.highlight { background: #ffffff; } +.highlight .c { color: #999988; font-style: italic } /* Comment */ +.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ +.highlight .k { font-weight: bold } /* Keyword */ +.highlight .o { font-weight: bold } /* Operator */ +.highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #999999; font-weight: bold } /* Comment.Preproc */ +.highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ +.highlight .gd .x { color: #000000; background-color: #ffaaaa } /* Generic.Deleted.Specific */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .gr { color: #aa0000 } /* Generic.Error */ +.highlight .gh { color: #999999 } /* Generic.Heading */ +.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ +.highlight .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */ +.highlight .go { color: #888888 } /* Generic.Output */ +.highlight .gp { color: #555555 } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #aaaaaa } /* Generic.Subheading */ +.highlight .gt { color: #aa0000 } /* Generic.Traceback */ +.highlight .kc { font-weight: bold } /* Keyword.Constant */ +.highlight .kd { font-weight: bold } /* Keyword.Declaration */ +.highlight .kp { font-weight: bold } /* Keyword.Pseudo */ +.highlight .kr { font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */ +.highlight .m { color: #009999 } /* Literal.Number */ +.highlight .s { color: #d14 } /* Literal.String */ +.highlight .na { color: #008080 } /* Name.Attribute */ +.highlight .nb { color: #0086B3 } /* Name.Builtin */ +.highlight .nc { color: #445588; font-weight: bold } /* Name.Class */ +.highlight .no { color: #008080 } /* Name.Constant */ +.highlight .ni { color: #800080 } /* Name.Entity */ +.highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #990000; font-weight: bold } /* Name.Function */ +.highlight .nn { color: #555555 } /* Name.Namespace */ +.highlight .nt { color: #000080 } /* Name.Tag */ +.highlight .nv { color: #008080 } /* Name.Variable */ +.highlight .ow { font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mf { color: #009999 } /* Literal.Number.Float */ +.highlight .mh { color: #009999 } /* Literal.Number.Hex */ +.highlight .mi { color: #009999 } /* Literal.Number.Integer */ +.highlight .mo { color: #009999 } /* Literal.Number.Oct */ +.highlight .sb { color: #d14 } /* Literal.String.Backtick */ +.highlight .sc { color: #d14 } /* Literal.String.Char */ +.highlight .sd { color: #d14 } /* Literal.String.Doc */ +.highlight .s2 { color: #d14 } /* Literal.String.Double */ +.highlight .se { color: #d14 } /* Literal.String.Escape */ +.highlight .sh { color: #d14 } /* Literal.String.Heredoc */ +.highlight .si { color: #d14 } /* Literal.String.Interpol */ +.highlight .sx { color: #d14 } /* Literal.String.Other */ +.highlight .sr { color: #009926 } /* Literal.String.Regex */ +.highlight .s1 { color: #d14 } /* Literal.String.Single */ +.highlight .ss { color: #990073 } /* Literal.String.Symbol */ +.highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */ +.highlight .vc { color: #008080 } /* Name.Variable.Class */ +.highlight .vg { color: #008080 } /* Name.Variable.Global */ +.highlight .vi { color: #008080 } /* Name.Variable.Instance */ +.highlight .il { color: #009999 } /* Literal.Number.Integer.Long */ diff --git a/documentation/getting-started/installation/index.markdown b/documentation/getting-started/installation/index.markdown new file mode 100644 index 00000000..944d33c2 --- /dev/null +++ b/documentation/getting-started/installation/index.markdown @@ -0,0 +1,86 @@ +--- +title: Installation +layout: default +--- + +Capistrano is bundled as a Ruby Gem. **It requires Ruby 1.9 or newer.** + +Capistrano can be installed as a standalone Gem, or bundled into your +application. + +
    +It is recommended to fix the version number when using Capistrano, and is +therefore recommended to use an appropriate bundler. +
    + +### General Usage + +The following commands will clone Capistrano at the latest `v3` revision, and +will build the gem and install it locally. **The Gem is not yet availalble via +Rubygems.org.** + +{% highlight bash %} +$ git clone -b v3 https://github.com/capistrano/capistrano.git +$ cd capistrano +$ gem build *.gemspec +$ gem install *.gem +{% endhighlight %} + +### Usage in a Rails project + +Add the following lines to the Gemfile, to the group `:development` ideally. + +{% highlight ruby %} +group :development do + gem 'capistrano-rails', github: 'capistrano/capistrano-rails', branch: 'v3' +end +{% endhighlight %} + +There will be a Gem released via [rubygems.org][rubygems], but as most people +are still using Capistrano v2.x, the v3 release will not be pushed to +[rubygems.org][rubygems] just yet give people a chance to lock their version +in their `Gemfile`. + +The *Capistrano-Rails* Gem includes extras specifically designed for Ruby on +Rails, specifically: + + * Asset Pipeline Support + * Gem Bundler Support + * Database Migration Support + +The documentation for these components can be found in +[their][capistrano-rails-asset-pipeline-readme], +[respective][capistrano-rails-gem-bundler-readme], +[READMEs][capistrano-rails-database-migrations-readme]. However for the most +part, to get the best, and most sensible results, simply `require` these +files: + +{% highlight ruby %} +require 'capistrano/rails/assets' +require 'capistrano/rails/gem-bundler' +require 'capistrano/rails/database-migrations' +{% endhighlight %} + +
    +
    Help! I was using Capistrano `v2.x` and I didn't want to upgrade!
    + +If you are using Capistrano `v2.x.x` and have also installed Capistrano `v3` +by mistake, then you can lock your Gem version for Capistrano at something +like: + +{% highlight ruby %} +gem 'capistrano', '~> 2.15' # Or whatever patch release you are using +{% endhighlight %} + +This is the [pessimistic operator][rubygems-pessimistic-operator] which +installs the closest matching version, at the time of writing this would +install `2.15.4`, and any other point-release in the `2.15.x` family without +the risk of accidentally upgrading to `v3`. +
    + +-- +[rubygems]: http://rubygems.org/ +[rubygems-pessimistic-operator]: http://docs.rubygems.org/read/chapter/16#page74 +[capistrano-rails-asset-pipeline-readme]: https://www.github.com/capistrano/asset-pipeline/bundler/README.md +[capistrano-rails-database-migrations-readme]: https://www.github.com/capistrano/migrations/bundler/README.md +[capistrano-rails-gem-bundler-readme]: https://www.github.com/capistrano/capistrano-rails/bundler/README.md diff --git a/documentation/index.html b/documentation/index.html new file mode 100644 index 00000000..879ae949 --- /dev/null +++ b/documentation/index.html @@ -0,0 +1 @@ +FUCK YOU JEKYLL \ No newline at end of file diff --git a/documentation/overview/introductory-demo-video/index.markdown b/documentation/overview/introductory-demo-video/index.markdown new file mode 100644 index 00000000..f7c5e04f --- /dev/null +++ b/documentation/overview/introductory-demo-video/index.markdown @@ -0,0 +1,38 @@ +--- +title: Introductory Demo Video +layout: default +--- + +The video below was filmed on Mac OSX 10.8 using a more-or-less standard shell +without much previous setup. + +It covers using Capistrano to install an example Rails project on a previously +unprepared server, covering all aspects of Github access, as well as +privisioning the server using *Chef Solo* and Capistrano with *Rake*. + + + +#### Show Notes + +The *Chef Solo* recipes can be reached at [this repository at +Github][capistrano-chef-solo-example-recipes], they rely on a fairly new +version of *Chef Solo*, spefically any including the results of [this +ticket][chef-issue-3365]. The aforementioned *Chef* issue adds environment +support to *Chef Solo*. + +The provisioning can also be done using any other mechanism, it's generally +accepted however that there's not much point in automising your deploys, +unless you are also automating provisioning of your servers for a known, +consistent state. + +Using `sudo` with any deployment can be tricky, so it's better to avoid it. +Rebooting services without `sudo` is typically the first place people run into +trouble using Capistrano. The [trouble shooting page for `sudo` +problems][troubleshooting-sudo-password] may help. + +**Note:** Some long sequences have been shortened (nobody needs to sit and watch me +sitting and watching Ruby compile, for example!) + +-- +[chef-issue-3365]: https://github.com/opscode/chef/pull/359 +[troubleshooting-sudo-password]: /troubleshooting/sudo-password/ diff --git a/documentation/upgrading/index.html b/documentation/upgrading/index.html new file mode 100644 index 00000000..4937e7e3 --- /dev/null +++ b/documentation/upgrading/index.html @@ -0,0 +1,5 @@ +--- +title: "Upgrading from v2.x.x" +--- + +Lorem Ipsum diff --git a/images/CapistranoAvatar.png b/images/CapistranoAvatar.png new file mode 100644 index 00000000..d4acd443 Binary files /dev/null and b/images/CapistranoAvatar.png differ diff --git a/images/CapistranoGraphic.png b/images/CapistranoGraphic.png new file mode 100644 index 00000000..c42ee367 Binary files /dev/null and b/images/CapistranoGraphic.png differ diff --git a/images/CapistranoGraphicWireframe.png b/images/CapistranoGraphicWireframe.png new file mode 100644 index 00000000..16c4c269 Binary files /dev/null and b/images/CapistranoGraphicWireframe.png differ diff --git a/images/CapistranoLogo.png b/images/CapistranoLogo.png new file mode 100644 index 00000000..d41fa2dc Binary files /dev/null and b/images/CapistranoLogo.png differ diff --git a/index.markdown b/index.markdown new file mode 100644 index 00000000..e2031f03 --- /dev/null +++ b/index.markdown @@ -0,0 +1,35 @@ +--- +layout: default +title: A remote server automation and deployment tool written in Ruby. +--- + +### A Simple Task + +{% highlight ruby %} +role :demo, %{example.com example.org, example.net} +task :uptime do |host| + on :demo, in: :parallel do + uptime = capture(:uptime) + puts "#{host.hostname} reports: #{uptime}" + end +end +{% endhighlight %} + +Capistrano extends the *Rake* DSL with methods specific to running commands +`on()` servers. + +### For Any Language + +Capistrano is written in Ruby, but it can easily be used to deploy any +language. Popular extensions add support for *Wordpress* blogs as well as +*Symfony* and *Node.js* applications. + +If your language has special deployment requirements, Capistrano can easily be +extended to support them. + +### Demo Video + + +