Skip to content

Instantly share code, notes, and snippets.

@dv
Created April 10, 2014 11:27
Show Gist options
  • Save dv/10370719 to your computer and use it in GitHub Desktop.
Save dv/10370719 to your computer and use it in GitHub Desktop.
Capistrano 3 + Rails 4 + Whenever + DelayedJob + Nginx + Postgresql
# Load DSL and Setup Up Stages
require 'capistrano/setup'
# Includes default deployment tasks
require 'capistrano/deploy'
# Rails (includes bundler, rails/assets and rails/migrations)
require 'capistrano/rails'
# Whenever
require "whenever/capistrano"
# Includes tasks from other gems included in your Gemfile
#
# For documentation on these, see for example:
#
# https://github.com/capistrano/rvm
# https://github.com/capistrano/rbenv
# https://github.com/capistrano/chruby
# https://github.com/capistrano/bundler
# https://github.com/capistrano/rails
#
# require 'capistrano/rvm'
# require 'capistrano/rbenv'
# require 'capistrano/chruby'
# require 'capistrano/bundler'
# require 'capistrano/rails/assets'
# require 'capistrano/rails/migrations'
# Loads custom tasks from `lib/capistrano/tasks' if you have any defined.
Dir.glob('lib/capistrano/tasks/*.cap').each { |r| import r }
# lib/capistrano/tasks/helpers/capistrano_helpers.rb
def find_template(name)
File.expand_path("../../templates/#{name}", __FILE__)
end
def find_config(name)
File.expand_path("../../../../../config/#{name}", __FILE__)
end
def template(from, to)
erb = File.read(find_template(from))
upload! StringIO.new(ERB.new(erb).result(binding)), "/tmp/cap-template.tmp"
sudo :cp, "/tmp/cap-template.tmp", to
end
def stage
fetch(:stage)
end
def domain
fetch(:domain)
end
def production?
stage == :production
end
def staging?
stage == :staging
end
def database_config
yaml = YAML.load_file find_config("database.yml")
yaml[fetch(:stage).to_s]
end
# lib/capistrano/tasks/delayed_job.cap
namespace :delayed_job do
def args
fetch(:delayed_job_args, "")
end
def delayed_job_roles
fetch(:delayed_job_server_role, :app)
end
desc 'Stop the delayed_job process'
task :stop do
on roles(delayed_job_roles) do
within release_path do
with rails_env: fetch(:rails_env) do
execute :bundle, :exec, :'bin/delayed_job', :stop
end
end
end
end
desc 'Start the delayed_job process'
task :start do
on roles(delayed_job_roles) do
within release_path do
with rails_env: fetch(:rails_env) do
execute :bundle, :exec, :'bin/delayed_job', args, :start
end
end
end
end
desc 'Restart the delayed_job process'
task :restart do
on roles(delayed_job_roles) do
within release_path do
with rails_env: fetch(:rails_env) do
execute :bundle, :exec, :'bin/delayed_job', args, :restart
end
end
end
end
end
# lib/capistrano/tasks/deploy.cap
namespace :deploy do
desc "Creates the deploy-to directory."
task :create_deploy_to_dir do
on roles(:web) do
sudo :mkdir, "-p", fetch(:deploy_to)
sudo :chown, "david:www-data", fetch(:deploy_to)
sudo :chmod, "g+s", fetch(:deploy_to)
end
end
before "deploy:check", "deploy:create_deploy_to_dir"
desc "Configures the webserver and database"
task :config do
invoke "deploy:config:nginx"
invoke "deploy:config:postgresql"
end
namespace :config do
desc "Configure Nginx"
task :nginx do
invoke "nginx:setup"
end
desc "Configure Postgresql"
task :postgresql do
invoke "postgresql:create_db"
end
end
end
# config/deploy.rb
# config valid only for Capistrano 3.1
lock '3.1.0'
set :application, 'your-app-name'
set :domain, 'your-domain.com'
server 'your-domain.com', user: 'david', roles: %w{web app db}
set :repo_url, '[email protected]:your-git-name/your-app-name.git'
# Default value for :linked_files is []
# set :linked_files, %w{config/database.yml}
# Default value for linked_dirs is []
set :linked_dirs, %w{log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system}
# Default value for default_env is {}
# set :default_env, { path: "/opt/ruby/bin:$PATH" }
# Default value for keep_releases is 5
# set :keep_releases, 5
namespace :deploy do
desc 'Restart application'
task :restart do
on roles(:app), in: :sequence, wait: 5 do
execute :touch, release_path.join('tmp/restart.txt')
end
invoke 'delayed_job:restart'
end
after :publishing, :restart
after :restart, :clear_cache do
on roles(:web), in: :groups, limit: 3, wait: 10 do
# Here we can do anything such as:
# within release_path do
# execute :rake, 'cache:clear'
# end
end
end
end
# lib/capistrano/tasks/templates/nginx-site.config.production.erb
server {
server_name <%= fetch :domain %> www.<%= fetch :domain %>;
access_log /var/log/nginx/<%= fetch :application %>.access.log;
error_log /var/log/nginx/<%= fetch :application %>.error.log;
root <%= fetch :deploy_to %>/current/public;
autoindex off;
include global/restrictions.conf;
error_page 403 = /404.html;
error_page 404 = /404.html;
error_page 422 = /422.html;
error_page 500 = /500.html;
location / {
rack_env production;
passenger_min_instances 10;
passenger_enabled on;
}
location /assets/ {
gzip_static on;
expires 3M;
add_header Cache-Control public;
}
}
# lib/capistrano/tasks/templates/nginx-site.config.staging.erb
server {
server_name staging.<%= fetch :domain %>;
access_log /var/log/nginx/<%= fetch :application %>.staging.access.log;
error_log /var/log/nginx/<%= fetch :application %>.staging.error.log;
root <%= fetch :deploy_to %>/current/public;
include global/password_protected.conf;
include global/restrictions.conf;
rack_env staging;
passenger_enabled on;
passenger_min_instances 2;
error_page 403 = /404.html;
error_page 404 = /404.html;
error_page 422 = /422.html;
error_page 500 = /500.html;
}
# lib/capistrano/tasks/nginx.cap
require_relative "helpers/capistrano_helpers.rb"
namespace :nginx do
desc "Generate and upload Nginx site config file"
task :setup do
on roles(:web) do
if production?
filename = domain
else
filename = stage.to_s + "." + domain
end
template "nginx-site.conf.#{stage}.erb", "/etc/nginx/sites-enabled/#{filename}"
end
invoke "nginx:restart"
end
# after "deploy:setup", "nginx:setup"
%w[start stop restart].each do |command|
desc "#{command} nginx"
task command do
on roles(:web) do
execute :sudo, :service, "nginx", command
end
end
end
end
# lib/capistrano/tasks/postgresql.cap
namespace :postgresql do
desc "Configure the database and role for this application"
task :config do
if database_config["adapter"] == "postgresql"
invoke "postgresql:create_db"
invoke "postgresql:create_role"
else
raise "Not using postgresql"
end
end
desc "Create the database for this application"
task :create_db do
on primary(:db) do
if test %Q{sudo -u postgres psql -lqt | cut -d \\| -f 1 | grep -wq #{database_config["database"]}}
info "Database #{database_config['database']} already exists"
else
execute %Q{sudo -u postgres psql -c "create user #{database_config["username"]} with password '#{database_config["password"]}';"}
end
end
end
desc "Create the database role for this application"
task :create_role do
on primary(:db) do
if test %Q{sudo -u postgres psql postgres -tAc "SELECT 1 FROM pg_roles WHERE rolname='#{database_config["username"]}';" | grep -q 1}
info "Role #{database_config['username']} already exists"
else
execute %Q{sudo -u postgres psql -c "create user #{database_config["username"]} with password '#{database_config["password"]}';"}
end
end
end
end
# config/deploy/production.rb
set :deploy_to, '/var/http/your-app-name/production'
set :rails_env, 'production'
set :branch, 'production'
# config/deploy/staging.rb
set :deploy_to, '/var/http/your-app-name/staging'
set :rails_env, 'staging'
set :branch, 'master'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment