Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to run schema:load on the initial capistrano 3 deploy of my rails app

I would like to run db:schema:load in place of db:migrate on the initial deploy of my rails app.

This used to be fairly trivial, as seen in this stack overflow question, but in Capistrano 3, they have deprecated the deploy:cold task. The initial deploy isn't any different than all subsequent deploys.

Any suggestions? Thanks!

like image 533
Adam Albrecht Avatar asked Dec 16 '13 21:12

Adam Albrecht


2 Answers

I, too, am new to Capistrano, and trying to use it for the first time to deploy a Rails app to production servers I configured with Puppet.

I finally had to dig into the Capistrano source (and capistrano/bundler, and capistrano/rails, and even sshkit and net-ssh to debug auth problems) to determine exactly how everything works before I felt confidant deciding for myself what changes I wanted to make. I just finished making those changes, and I'm pleased with the results:

# lib/capistrano/tasks/cold.rake
namespace :deploy do

  desc "deploy app for the first time (expects pre-created but empty DB)"
  task :cold do
    before 'deploy:migrate', 'deploy:initdb'
    invoke 'deploy'
  end

  desc "initialize a brand-new database (db:schema:load, db:seed)"
  task :initdb do
    on primary :web do |host|
      within release_path do
        if test(:psql, 'portal_production -c "SELECT table_name FROM information_schema.tables WHERE table_schema=\'public\' AND table_type=\'BASE TABLE\';"|grep schema_migrations')
          puts '*** THE PRODUCTION DATABASE IS ALREADY INITIALIZED, YOU IDIOT! ***'
        else
          execute :rake, 'db:schema:load'
          execute :rake, 'db:seed'
        end
      end
    end
  end

end

The deploy:cold task merely hooks my custom deploy:inidb task to run before deploy:migrate. That way the schema and seeds get loaded, and the deploy:migrate step that follows does nothing (safely) because there are no new migrations to run. As a safety, I test to see if the schema_migrations table already exists before loading the schema in case you run deploy:cold again.

Note: I choose to create the DB using Puppet so I can avoid having to grant the CREATEDB privilege to my production postgresql user, but if you want Capistrano to do it, just add "execute :rake, 'db:create'" before the db:schema:load, or replace all three lines with 'db:setup'.

like image 164
odigity Avatar answered Sep 27 '22 23:09

odigity


You'll have to define deploy:cold as basically a duplicate of the normal deploy task but with deploy:db_load_schema instead of deploy:migrations. For example:

desc 'Deploy app for first time'
task :cold do
  invoke 'deploy:starting'
  invoke 'deploy:started'
  invoke 'deploy:updating'
  invoke 'bundler:install'
  invoke 'deploy:db_load_schema' # This replaces deploy:migrations
  invoke 'deploy:compile_assets'
  invoke 'deploy:normalize_assets'
  invoke 'deploy:publishing'
  invoke 'deploy:published'
  invoke 'deploy:finishing'
  invoke 'deploy:finished'
end

desc 'Setup database'
task :db_load_schema do
  on roles(:db) do
    within release_path do
      with rails_env: (fetch(:rails_env) || fetch(:stage)) do
        execute :rake, 'db:schema:load'
      end
    end
  end
end

It might even be better to run the deploy:db_schema_load task independently, as the tasks included in the default deploy might change over time.

I actually using db:setup for fresh deploys because it seeds the database after creating tables:

desc 'Setup database'
task :db_setup do
  ...
        execute :rake, 'db:setup'
  ...
end
like image 41
Xavier Avatar answered Sep 28 '22 00:09

Xavier