heroku_san icon indicating copy to clipboard operation
heroku_san copied to clipboard

For Rails apps, only run migrations and restart after deploy if migrations pending

Open lyahdav opened this issue 12 years ago • 3 comments

This only works for postgresql setups. Fixes https://github.com/fastestforward/heroku_san/issues/5.

lyahdav avatar Jul 24 '13 01:07 lyahdav

I should add, the reason I implemented this as a memoized function on the Stage class is so that it can easily be used in the before_deploy and after_deploy hooks. We need this on a Heroku project where we enable maintenance mode and do a couple other things in the before_deploy if there are pending migrations. Then in the after_deploy we clean up (disable maintenance mode) if there were pending migrations.

lyahdav avatar Jul 24 '13 01:07 lyahdav

This is an awesome feature and one I would find really useful too! I'm not sure why but the psql command in your pull request didn't work for me. This worked for me (basically just copied what the heroku toolbelt was doing):

def remote_migrations
  database_uri = URI.parse(long_config['DATABASE_URL'])
  begin
    ENV['PGPASSWORD'] = database_uri.password
    ENV['PGSSLMODE']  = 'require'
    psql_cmd = "psql -U #{database_uri.user} -h #{database_uri.host} " \
        "-p #{database_uri.port || 5432} #{database_uri.path[1..-1]}"
    `#{psql_cmd} -Atc "SELECT * FROM schema_migrations;"`.split.map(&:to_i)
  ensure
    ENV.delete('PGPASSWORD')
    ENV.delete('PGSSLMODE')
  end
end

This approach has the added benefit of being slightly more secure since the database password isn't included in the psql command line.

Another thing I found useful was basing the state of required migrations on the migration source code rather than the state of my local database (which can occasionally get out of sync when switching between branches). With the approach the change looks more like this (leaving out your checking for Rails and Postgres for brevity):

# Returns true if the remote DB has pending migrations and false if not.
def has_pending_migrations
  @has_pending_migrations ||= (local_migrations - remote_migrations).present?
end

private

# returns an array of schema migration versions in the local source directory
def local_migrations
  ActiveRecord::Migrator.migrations(ActiveRecord::Migrator.migrations_paths).map(&:version)
end

I'm curious to hear your thoughts on this approach.

jturkel avatar Aug 08 '13 13:08 jturkel

Sorry for taking long to respond. I like your changes. I'll work on making those changes now.

lyahdav avatar Aug 20 '13 20:08 lyahdav