How I setup Sinatra with Rake on Thin server and deployed it with Capistrano via GitHub

Here comes the tale of Sinatra’s deployment.

Note: I will pretend your project is in ~/myapp for these examples.

Note: I will also assume you’re using TextMate for file editing.

Step 1. Directory structure. Boring.

In this step we’re gonna setup a directory structure. The only reason you’re brutally forced to stick with this particular directory structure is no reason. (Except it helps to follow this tutorial.)

myapp/
  lib/
    tasks/
  log/
  test/
  thin/
  tmp/

Step 2. Get Sinatra.

cd ~/myapp/lib
git clone git://github.com/bmizerany/sinatra.git
rm -rf sinatra/.git
rm sinatra/.gitignore

I got rid of .git here because we’re gonna be versioning the whole app including Sinatra. Or maybe I just don’t know a better way to approach this.

Step 3. Write your app.

I call my main app file init.rb because that’s what Rails plugins use for entry file’s name. Essentially, I have no legitimate reason whatsoever for choosing this filename.

mate ~/myapp/init.rb

Put these requires in the beginning of your init.rb

require 'rubygems'
require File.expand_path(File.dirname(__FILE__) + '/lib/sinatra/lib/sinatra')
require File.expand_path(File.dirname(__FILE__) + '/lib/config')

# Put Sinatra code here

And now the config file.

mate ~/myapp/lib/config.rb
configure do
  # config stuff for you Sinatra app
end

I’m sure you have tests, right? Don’t forget to put your tests into ~/myapp/test dir.

Step 4. Setup rake and make it run tests.

mate ~/myapp/Rakefile

Put this into your Rakefile. The last line handily picks up all the tasks from lib/tasks.

require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'

Dir["#{File.dirname(__FILE__)}/lib/tasks/**/*.rake"].sort.each { |ext| load ext }

Now let’s create the default task for running tests.

mate ~/myapp/lib/tasks/misc.rake

Put this into misc.rake

task :default do
  # just run tests, nothing fancy
  Dir["test/**/*.rb"].sort.each { |test|  load test }
end

This makes it possible to run tests by simply typing rake in the root of myapp.

Step 5. Setup thin.

sudo gem install thin

Now let’s hang Sinatra on a Rack.

This is gonna be our so-called rackup file necessary for thin to pick up Sinatra app.

mate ~/myapp/thin/config.ru

Let’s put this into config.ru

require 'lib/sinatra/lib/sinatra'
 
Sinatra::Application.default_options.merge!(
  :run => false,
  :env => :production
)
 
require 'init'
run Sinatra.application

Awesome. Now let’s setup a config file.

mate ~/myapp/thin/development_config.yml

Use this code as a template, and change it into whatever is appropriate for your system.

---
   environment: development
   chdir: /Users/[your_user_name]/myapp
   address: 127.0.0.1
   port: 4567
   pid: /Users/[your_user_name]/myapp/tmp/thin.pid
   rackup: /Users/[your_user_name]/myapp/thin/config.ru
   log: /Users/[your_user_name]/myapp/log/thin.log
   max_conns: 1024
   timeout: 30
   max_persistent_conns: 512
   daemonize: true

I also have production_config.yml file in my thin dir. It looks like this.

---
   environment: development
   chdir: /path/to/app/current/myapp
   address: 127.0.0.1
   user: root
   group: root
   port: 4567
   pid: /path/to/app/current/myapp/tmp/thin.pid
   rackup: /path/to/app/current/myapp/thin/config.ru
   log: /path/to/app/current/myapp/log/thin.log
   max_conns: 1024
   timeout: 30
   max_persistent_conns: 512
   daemonize: true

Notice that all the paths have /current in them. That’s necessary for Capistrano deployment.

Ok now you should be able to start your thin server.

cd ~/myapp
nohup thin -C thin/development_config.yml -R thin/config.ru start

Check that you have it running on http://localhost:4567

You can stop thin just as easily.

cd ~/myapp
nohup thin -C thin/development_config.yml -R thin/config.ru stop

Step 6. Git.

Go to github.com and setup your new repository.

Then create .gitignore, to prevent crappyness from being versioned.

mate ~/myapp/.gitignore

Here’s what I ignore.

log/*.log
log/*.pid
tmp/**/*
.DS_Store
thin/development_config.yml
lib/sinatra/*.log
lib/sinatra/dist
lib/sinatra/book
lib/sinatra/doc/api
lib/sinatra/doc/*.html

And finally, let’s perform the usual git kung fu action.

cd ~/myapp
git init
git remote add origin git@github.com:[your_username]/[your_repo].git
git add .
git commit -m "initial commit"
git push origin master

Step 7. Capistrano.

mate ~/myapp/Capfile

Put this into your Capfile.

load 'deploy' if respond_to?(:namespace)
load 'lib/deploy'

Now is the fun part! Let’s write the Capistrano deployment recipe.

mate ~/lib/deploy.rb

And here’s the deploy.rb

set :application, "example.com"
set :user, "root" # I used root, less problems, but not recommended.

set :scm, :git

# This distinction is necessary if the way you access github locally
# is different from the way your production environment will access it.
# For me it was the case.
set :local_repository, "git@github.com:[your_username]/[your_repo].git"
set :repository, "git@github.com:[your_username]/[your_repo].git"

set :deploy_to, "/path/to/app" # path to app on remote machine
set :deploy_via, :remote_cache # quicker checkouts from github

set :domain, '255.255.255.255' # your remote machine's domain name goes here
role :app, domain
role :web, domain

set :runner, user
set :admin_runner, runner

namespace :deploy do
  task :start, :roles => [:web, :app] do
    run "cd #{deploy_to}/current && nohup thin -C thin/production_config.yml -R thin/config.ru start"
  end

  task :stop, :roles => [:web, :app] do
    run "cd #{deploy_to}/current && nohup thin -C thin/production_config.yml -R thin/config.ru stop"
  end

  task :restart, :roles => [:web, :app] do
    deploy.stop
    deploy.start
  end
 
  # This will make sure that Capistrano doesn't try to run rake:migrate (this is not a Rails project!)
  task :cold do
    deploy.update
    deploy.start
  end
end

Step 8. Deployment.

cd ~/myapp
git add .
git commit -m "New deployment recipe."
git push origin master
cap deploy:setup
cap deploy:cold

In an ideal world the above should pretty much do it. Flowers should start blooming, butterflies should start flapping their wings, and your app should come alive. In the real world – you gonna be suffering with a lot of rough edges. Oh well, every system is different. At least this is some kind of roadmap. Hope it’s been mildly helpful.

2 responses to “How I setup Sinatra with Rake on Thin server and deployed it with Capistrano via GitHub”

  1. pjammer

    my friend! you are a gentleman and a scholar! where did you find: Dir["#{File.dirname(FILE)}/lib/tasks/**/*.rake"].sort.each { |ext| load ext } in that Rakefile part. It’s exactly what i needed!!!!

    Thanks so much.

  2. Juan C. Mendez

    Really nice write-up

    One thing – on the config.ru it should be

    run Sinatra::Application

    instead of

    run Sinatra.application

Leave a Reply