Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions lib/heroku_san/deploy/rails.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ class Rails < Base
def deploy
# TODO: Add announce/logger
super
@stage.run('rake db:migrate')
@stage.restart
if @stage.has_pending_migrations
@stage.run('rake db:migrate')
@stage.restart
end
end
end
end
end
end

40 changes: 40 additions & 0 deletions lib/heroku_san/stage.rb
Original file line number Diff line number Diff line change
Expand Up @@ -129,5 +129,45 @@ def logs(tail = false)
def revision
git_named_rev(git_revision(repo))
end

# Will return true if not using Rails or the remote DB isn't postgresql.
# Otherwise returns true if the remote DB has pending migrations and false if not.
def has_pending_migrations
return @has_pending_migrations if @has_pending_migrations != nil
return true unless defined?(::Rails)
database_url = long_config['DATABASE_URL']
return true unless database_url =~ /\Apostgres:\/\//
@has_pending_migrations = (local_migrations - remote_migrations(database_url)).present?
end

private

# returns an array of the schema_migrations run on the server
def remote_migrations(database_url)
database_uri = URI.parse(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_output = `#{psql_cmd} -Atc "SELECT * FROM schema_migrations;" 2>&1`
if psql_output =~ /ERROR: relation "schema_migrations" does not exist/
puts "'schema_migrations' table doesn't exist remotely, assuming no remote migrations"
return []
end
raise 'Error getting remote migrations' unless $?.success?
psql_output.split.map(&:to_i)
ensure
ENV.delete('PGPASSWORD')
ENV.delete('PGSSLMODE')
end
end

# returns an array of schema migration versions in the local source directory
def local_migrations
Rake::Task[:environment].invoke
ActiveRecord::Base.establish_connection
ActiveRecord::Migrator.migrations(ActiveRecord::Migrator.migrations_paths).map(&:version)
end

end
end
10 changes: 10 additions & 0 deletions spec/heroku_san/deploy/rails_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,20 @@ module Deploy
it "calls push, rake db:migrate & restart" do
subject = described_class.new(stage, {})
stage.should_receive(:push) { "pushed" } # "mock" super
stage.should_receive(:has_pending_migrations) { true }
stage.should_receive(:run).with('rake db:migrate') { "migrated" }
stage.should_receive(:restart) { "restarted" }
subject.deploy
end

it "skips rake db:migrate & restart if there are no pending migrations" do
subject = described_class.new(stage, {})
stage.should_receive(:push) { "pushed" } # "mock" super
stage.should_receive(:has_pending_migrations) { false }
stage.should_not_receive(:run).with('rake db:migrate')
stage.should_not_receive(:restart)
subject.deploy
end
end
end
end