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.)
lib/
tasks/
log/
test/
thin/
tmp/
Step 2. Get Sinatra.
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.
Put these requires in the beginning of your init.rb
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.
# 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.
Put this into your Rakefile. The last line handily picks up all the tasks from lib/tasks.
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.
Put this into misc.rake
# 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.
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.
Let’s put this into config.ru
Sinatra::Application.default_options.merge!(
:run => false,
:env => :production
)
require 'init'
run Sinatra.application
Awesome. Now let’s setup a config file.
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.
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.
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.
Here’s what I ignore.
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.
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.
Put this into your Capfile.
load 'lib/deploy'
Now is the fun part! Let’s write the Capistrano deployment recipe.
And here’s the deploy.rb
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.
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.
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.
Really nice write-up
One thing – on the config.ru it should be
run Sinatra::Application
instead of
run Sinatra.application