Skip to content

Commit e9d835f

Browse files
committed
Fix pending migrations error message
While this was checking each connection for pending migrations it was also raising as soon as it hit any pending migration. Instead we want this to output all current pending migrations in each directory. This change loops through all the connections to collect pending migrations and then passes them to the error message handling if applicable. Instead of changing the public `check_pending!`, I stopped calling that from `check_pending_migrations` and raise the error directory from `check_pending_migrations`. I also updated `check_pending` to pass pending migrations from the passed connection. I think evenutally we'll want to deprecate `check_pending!`, it's not very useful without the other connections but since it's public I avoided changing it.
1 parent 07f48f7 commit e9d835f

File tree

2 files changed

+41
-28
lines changed

2 files changed

+41
-28
lines changed

activerecord/lib/active_record/migration.rb

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -143,18 +143,20 @@ class PendingMigrationError < MigrationError # :nodoc:
143143
end
144144
end
145145

146-
def initialize(message = nil)
147-
super(message || detailed_migration_message)
146+
def initialize(message = nil, pending_migrations: nil)
147+
if pending_migrations.nil?
148+
pending_migrations = ActiveRecord::Base.connection.migration_context.open.pending_migrations
149+
end
150+
151+
super(message || detailed_migration_message(pending_migrations))
148152
end
149153

150154
private
151-
def detailed_migration_message
155+
def detailed_migration_message(pending_migrations)
152156
message = "Migrations are pending. To resolve this issue, run:\n\n bin/rails db:migrate"
153157
message += " RAILS_ENV=#{::Rails.env}" if defined?(Rails.env) && !(Rails.env.development? || Rails.env.test?)
154158
message += "\n\n"
155159

156-
pending_migrations = ActiveRecord::Base.connection.migration_context.open.pending_migrations
157-
158160
message += "You have #{pending_migrations.size} pending #{pending_migrations.size > 1 ? 'migrations:' : 'migration:'}\n\n"
159161

160162
pending_migrations.each do |pending_migration|
@@ -634,7 +636,9 @@ def nearest_delegate # :nodoc:
634636

635637
# Raises <tt>ActiveRecord::PendingMigrationError</tt> error if any migrations are pending.
636638
def check_pending!(connection = Base.connection)
637-
raise ActiveRecord::PendingMigrationError if connection.migration_context.needs_migration?
639+
if pending_migrations = connection.migration_context.pending_migrations
640+
raise ActiveRecord::PendingMigrationError.new(pending_migrations: pending_migrations)
641+
end
638642
end
639643

640644
def load_schema_if_pending!
@@ -678,17 +682,28 @@ def disable_ddl_transaction!
678682
end
679683

680684
def check_pending_migrations # :nodoc:
681-
prev_db_config = Base.connection_db_config
682-
683-
db_configs_in_current_env.each do |db_config|
684-
Base.establish_connection(db_config)
685-
check_pending!
685+
if pending_migrations.any?
686+
raise ActiveRecord::PendingMigrationError.new(pending_migrations: pending_migrations)
686687
end
687-
ensure
688-
Base.establish_connection(prev_db_config)
689688
end
690689

691690
private
691+
def pending_migrations
692+
prev_db_config = Base.connection_db_config
693+
pending_migrations = []
694+
695+
db_configs_in_current_env.each do |db_config|
696+
conn = Base.establish_connection(db_config).connection
697+
if pending = conn.migration_context.open.pending_migrations
698+
pending_migrations << pending
699+
end
700+
end
701+
702+
pending_migrations.flatten
703+
ensure
704+
Base.establish_connection(prev_db_config)
705+
end
706+
692707
def db_configs_in_current_env
693708
current_environment = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
694709
ActiveRecord::Base.configurations.configs_for(env_name: current_environment)

activerecord/test/cases/migration/pending_migrations_test.rb

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class #{name.classify} < ActiveRecord::Migration::Current
4141

4242
def test_errors_if_pending
4343
create_migration "01", "create_foo"
44-
assert_pending_migrations
44+
assert_pending_migrations("01_create_foo.rb")
4545
end
4646

4747
def test_checks_if_supported
@@ -62,26 +62,21 @@ def test_understands_migrations_created_out_of_order
6262

6363
# It understands the new migration created at 01
6464
create_migration "01", "create_foo"
65-
assert_pending_migrations
65+
assert_pending_migrations("01_create_foo.rb")
6666
end
6767

6868
def test_with_multiple_database
69-
create_migration "01", "create_bar", database: :secondary
70-
assert_pending_migrations
69+
create_migration "01", "create_bar", database: :primary
70+
create_migration "02", "create_foo", database: :secondary
71+
assert_pending_migrations("01_create_bar.rb", "02_create_foo.rb")
7172

7273
ActiveRecord::Base.establish_connection(:secondary)
7374
quietly { run_migrations }
7475

7576
ActiveRecord::Base.establish_connection(:primary)
77+
quietly { run_migrations }
7678

7779
assert_no_pending_migrations
78-
79-
# Now check exclusion if database_tasks is set to false for the db_config
80-
create_migration "02", "create_foo", database: :secondary
81-
assert_pending_migrations
82-
83-
ActiveRecord::Base.configurations = base_config(secondary: { database_tasks: false })
84-
assert_no_pending_migrations
8580
end
8681

8782
def test_with_stdlib_logger
@@ -92,20 +87,23 @@ def test_with_stdlib_logger
9287
end
9388

9489
private
95-
def assert_pending_migrations
96-
# Do twice to test that the error continues to be raised.
90+
def assert_pending_migrations(*expected_migrations)
9791
2.times do
98-
assert_raises ActiveRecord::PendingMigrationError do
92+
error = assert_raises ActiveRecord::PendingMigrationError do
9993
CheckPending.new(proc { flunk }).call({})
10094
end
95+
96+
assert_includes error.message, "Migrations are pending."
97+
expected_migrations.each do |migration|
98+
assert_includes error.message, migration
99+
end
101100
end
102101
end
103102

104103
def assert_no_pending_migrations
105104
app = Minitest::Mock.new
106105
check_pending = CheckPending.new(app)
107106

108-
# Do twice to also test the cached result.
109107
2.times do
110108
app.expect :call, nil, [{}]
111109
check_pending.call({})

0 commit comments

Comments
 (0)