RyanDoyle.net

home posts contact

Embedded Capistrano with SSHKit

29 Apr 2014

Capistrano is great for remote server automation over SSH but often I find myself only needing small parts from what the full Capistrano suite has to offer. Starting with Capistrano 3, the SSH-part of Capistrano was split out into a separate project called SSHKit.

SSHKit comes with a neat DSL that we can easily embed in our Rakefiles. Lets go through an example of how I deploy my Jekyll-powered blog via Rake:

# Rakefile
require 'yaml'
require 'sshkit'
require 'sshkit/dsl'

config = YAML.load_file('_config.yml')

deploy_server = config['deploy_server']
deploy_path = config['deploy_path']
deploy_user = config['deploy_user']

SSHKit::Backend::Netssh.configure do |ssh|
  ssh.ssh_options = {
      user: deploy_user,
      auth_methods: ['publickey']
  }
end

desc 'Deploy the site to production'
task :deploy do

  run_locally do
    execute 'jekyll', 'build'
  end

  on deploy_server do
    unless test "[ -d #{deploy_path} ]"
      as :root do
        execute 'mkdir', '-p', deploy_path
      end
    end
    unless test "[ `stat -c %U #{deploy_path}` = '#{deploy_user}']"
      as :root do
        execute 'chown', '-R', deploy_user, deploy_path
      end
    end
    upload! "_site/", deploy_path, recursive: true
  end

end

The desc and task are just plain Rake keywords. The rest of the DSL is SSHKit. Lets clean the code up and extract a Blog class:

# Rakefile
require 'yaml'
require 'sshkit'
require 'sshkit/dsl'

config = YAML.load_file('_config.yml')

deploy_server = config['deploy_server']
deploy_path = config['deploy_path']
deploy_user = config['deploy_user']

SSHKit::Backend::Netssh.configure do |ssh|
  ssh.ssh_options = {
      user: deploy_user,
      auth_methods: ['publickey']
  }
end

class Blog
  def initialize(host, path, owner)
    @host = host
    @path = path
    @owner = owner
  end

  def deploy!
    compile
    upload
  end

  private

  def upload
    with_deploy_location_ready do
      @host.upload! "_site/", @path, recursive: true
    end
  end

  def with_deploy_location_ready
    create_deploy_path unless deploy_path_exists?
    chown_to_owner unless deploy_path_owned?
    yield
  end

  def deploy_path_exists?
    @host.test "[ -d #{@path} ]"
  end

  def create_deploy_path
    @host.as :root do
      @host.execute 'mkdir', '-p', @path
    end

  end

  def deploy_path_owned?
    @host.test "[ `stat -c %U #{@path}` = '#{@owner}']"
  end

  def chown_to_owner
    @host.as :root do
      @host.execute 'chown', '-R', @owner, @path
    end
  end

  def compile
    @host.run_locally { execute 'jekyll', 'build' }
  end

end

desc 'Deploy the site to production'
task :deploy do
  on deploy_server do
    Blog.new(self, deploy_path, deploy_user).deploy!
  end
end

That's pretty neat I think! For more documentation of SSHKit, have a look at its EXAMPLES.



comments powered by Disqus

2014 - Ryan Doyle - Powered by Jekyll