Skip to content

Instantly share code, notes, and snippets.

@chipotle
Last active February 8, 2018 18:54
Show Gist options
  • Save chipotle/5506641 to your computer and use it in GitHub Desktop.
Save chipotle/5506641 to your computer and use it in GitHub Desktop.
Capistrano deployment script for Laravel 4
# Capistrano Laravel 4 Deployment Tasks
# Watts Martin (layotl at gmail com)
# https://gist.github.com/chipotle/5506641
# updated 14-Aug-2013
# Assumptions:
#
# - You are using a .gitignore similar to Laravel's default, so your
# vendor directory and composer(.phar) are *not* under version control
# - Composer is installed as an executable at /usr/local/bin/composer
#
# If you don't have Composer installed globally, modify the appropriate task
# (:composer_install). Or just install Composer globally!
set :application, "Application Name"
set :repository, "git@github.com:username/project.git"
set :scm, :git
set :scm_username, "username"
role :web, "example.net"
role :app, "example.net"
role :db, "example.net", :primary => true
set :deploy_to, "/opt/nginx/sites/example.net"
set :deploy_via, :remote_cache
set :use_sudo, false
set :ssh_options, {:forward_agent => true}
set :copy_exclude, [".git", ".gitignore", ".tags", ".tags_sorted_by_file"]
set :keep_releases, 4
# Nginx requires the php_fpm:reload task; other servers may not
after :deploy, "php_fpm:reload"
namespace :deploy do
task :update do
transaction do
update_code
copy_config
composer_install
link_shared
fix_permissions
clear_cache
cleanup
end
end
task :finalize_update do
transaction do
run "chmod -R g+w #{releases_path}/#{release_name}"
symlink
end
end
task :symlink do
transaction do
run "ln -nfs #{current_release} #{deploy_to}/#{current_dir}"
end
end
desc "Link Laravel shared directories."
task :link_shared do
transaction do
run "ln -nfs #{shared_path}/system #{current_release}/public/system"
end
end
desc "Run Artisan migrate task."
task :migrate do
run "php #{current_release}/artisan migrate"
end
desc "Deploy and execute pending migrations."
task :migrations do
update_code
copy_config
composer_install
link_shared
fix_permissions
migrate
end
desc "Set Laravel storage directory world-writable."
task :fix_permissions do
transaction do
run "chmod -R a+w #{current_release}/app/storage"
end
end
desc "Install dependencies with Composer"
task :composer_install do
transaction do
run "cd #{current_release};/usr/local/bin/composer install --no-dev --no-progress"
end
end
desc "Copy server-specific configuration files."
task :copy_config do
transaction do
run "cp #{shared_path}/config/* #{current_release}/app/config/"
end
end
end
# This command is tested on Arch Linux; other distributions/OSes may need a
# different configuration (or may not require this at all).
namespace :php_fpm do
desc "Reload PHP-FPM (requires sudo access to systemctl)."
task :reload, :roles => :app do
run "sudo /usr/bin/systemctl reload-or-restart php-fpm"
end
end
# Database dump task adapted from https://gist.github.com/rgo/318312
namespace :db do
task :backup_name, :roles => :db do
now = Time.now
run "mkdir -p #{shared_path}/db_backups"
backup_time = [now.year, now.month, now.day, now.hour, now.min].join('-')
set :backup_file, "#{shared_path}/db_backups/#{database}-#{backup_time}.sql"
end
desc "Backup MySQL or PostgreSQL database to shared_path/db_backups"
task :dump, :roles => :db do
run("php -r '$db=include\"#{shared_path}/config/database.php\";echo json_encode($db,JSON_UNESCAPED_SLASHES);'") { |channel, stream, data| @environment_info = YAML.load(data) }
default = @environment_info['default']
connection = @environment_info['connections'][default]
dbuser = connection['username']
dbpass = connection['password']
database = connection['database']
dbhost = connection['host']
set :database, database
backup_name
if connection['driver'] == 'mysql'
run "mysqldump --add-drop-table -u #{dbuser} -h #{dbhost} -p #{database} | bzip2 -c > #{backup_file}.bz2" do |ch, stream, out |
ch.send_data "#{dbpass}\n" if out=~ /^Enter password:/
end
else
run "pg_dump -W -c -U #{dbuser} -h #{dbhost} #{database} | bzip2 -c > #{backup_file}.bz2" do |ch, stream, out |
ch.send_data "#{dbpass}\n" if out=~ /^Password:/
end
end
end
desc "Sync production database to your local workstation"
task :clone_to_local, :roles => :db, :only => {:primary => true} do
dump
get "#{backup_file}.bz2", "/tmp/#{application}.sql.bz2"
data = `php -r '$db=include"app/config/database.php";echo json_encode($db,JSON_UNESCAPED_SLASHES);'`
development_info = YAML.load(data)
default = development_info['default']
connection = development_info['connections'][default]
dbuser = connection['username']
dbpass = connection['password']
database = connection['database']
dbhost = connection['host']
if connection['driver'] == 'mysql'
run_str = "bzcat '/tmp/#{application}.sql.bz2' | mysql -u #{dbuser} --password='#{dbpass}' -h #{dbhost} #{database}"
else
run_str = "PGPASSWORD=#{dbpass} bzcat '/tmp/#{application}.sql.bz2' | psql -U #{dbuser} -h #{dbhost} #{database}"
end
%x!#{run_str}!
end
end
@williamli
Copy link

is railsless-deploy required in the cap file?

@williamli
Copy link

should you be linking a file level rollback (deploy:rollback) with a db level rollback (:laravel_rollback)?
IMHO you might not have db migration every time you update your laravel code
ref #101

what i find interesting is that you can use call 'php artisan down' before update and call 'php artisan up' after each update

@chipotle
Copy link
Author

chipotle commented Jul 2, 2013

Railsless-deploy isn't required, no.

And, you're right about the db migration. This is still a work in progress, tweaked when my assumptions collide with the real world. :) The current iteration (as of today) doesn't attempt to do any database rollback -- while that seemed like a good idea when I wrote it, in practice it didn't work. There should probably be a Capistrano task for doing that on remote servers manually here, though.

@cviebrock
Copy link

First, thanks for this. I've hooked it up with multistage and it works nicely, with a few tweaks.

However, it doesn't seem to respect the keep_releases setting. After several deployments, all my old releases were still there. You need to add the cleanup task to the end of the deploy:update:

    task :update do
        transaction do
            update_code
            copy_config
            composer_install
            link_shared
            fix_permissions
            clear_cache
            cleanup
        end
    end

@alexw23
Copy link

alexw23 commented Oct 30, 2013

@cviebrock mind posting how you got it to work in multistage ?

@chipotle
Copy link
Author

I've updated the original gist—finally!—based on @cviebrock's comment above. (As you might guess, I haven't been using this for a while...)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment