nandi 1.0.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1f30954127084e986e43871561e9104360c08b9ef5ba2349fb9a90d0a27e39a6
4
- data.tar.gz: f3dd20ea3aca9be5f09bf0d7305edc36a0b41427817c11d31b106ecde71881c3
3
+ metadata.gz: ec4e2b089c4336135b3899d3d01ac55fd0876fa5f76b22f1672fb494c2267fce
4
+ data.tar.gz: f66eac48b75fcf959bacc454197c6fb09c6332026ea08a2587e02f6fcc6049fe
5
5
  SHA512:
6
- metadata.gz: 6da2b0b61037337005292830c78561c169160304952192fcef8ee5f42034ba7a83df2d6f48d1df96b5f626e6672175ddd393905375568e13908db20fa188128a
7
- data.tar.gz: 32c6154eac3f2da24fd2aeeafbd687be1d62900f4f7557b7ab30ee42e2e0a7b3bfa55bb4eb0bace5518f2c60a35bf1e82a6473e7fbd961b4dc0480cc13c854fc
6
+ metadata.gz: adc1220776ae8fb445ba59c7755e6b80e3449897211485804461a02ebace759d22d098955e67d3ded835f9ff8871c72116b62e4c6aa7f7f9bd5a2f5ecaff3766
7
+ data.tar.gz: 9d6d92342078cc00360874bc73b8a87407497ef0c694aaefbb5b465215b3965063fab62b9ad753c937110231de7b368c48cabb829b004a510e98c423e333e63a
data/README.md CHANGED
@@ -336,6 +336,8 @@ Some schema changes need to be split across two migration files. Whenever you wa
336
336
 
337
337
  For some of the most common cases, we provide a Rails generator that generates both files for you.
338
338
 
339
+ All generators support multi-database mode with the `--database` option (see Multi-Database Support section below).
340
+
339
341
  ### Not-null checks
340
342
 
341
343
  To generate migration files for a not-null check, run this command:
@@ -394,12 +396,23 @@ rails generate nandi:foreign_key foos bar --name my_fk
394
396
  Nandi can be configured in various ways, typically in an initializer:
395
397
 
396
398
  ```rb
399
+ # Single database configuration
397
400
  Nandi.configure do |config|
401
+ config.migration_directory = "db/safe_migrations"
398
402
  config.lock_timeout = 1_000
399
403
  end
404
+
405
+ # Multi-database configuration (see Multi-Database Support section below)
406
+ Nandi.configure do |config|
407
+ config.register_database(:primary)
408
+ config.register_database(:analytics,
409
+ migration_directory: "db/analytics_safe_migrations",
410
+ output_directory: "db/analytics_migrate"
411
+ )
412
+ end
400
413
  ```
401
414
 
402
- The configuration parameters are as follows.
415
+ The configuration parameters are as follows for setting Nandi up for a single database.
403
416
 
404
417
  ### `access_exclusive_lock_timeout_limit` (Integer)
405
418
 
@@ -419,11 +432,11 @@ The default lock timeout for migrations. Can be overridden by way of the `set_lo
419
432
 
420
433
  ### `migration_directory` (String)
421
434
 
422
- The directory for Nandi migrations. Default: `db/safe_migrations`
435
+ The directory for Nandi migrations. Default: `db/safe_migrations`.
423
436
 
424
437
  ### `output_directory` (String)
425
438
 
426
- The directory for output files. Default: `db/migrate`
439
+ The directory for output files. Default: `db/migrate`.
427
440
 
428
441
  ### `renderer` (Class)
429
442
 
@@ -470,6 +483,142 @@ Parameters:
470
483
 
471
484
  `klass` (Class) — The class to initialise with the arguments to the method. It should define a `template` instance method which will return a subclass of Cell::ViewModel from the Cells templating library and a `procedure` method that returns the name of the method. It may optionally define a `mixins` method, which will return an array of `Module`s to be mixed into any migration that uses this method.
472
485
 
486
+ ## Multi-Database Support
487
+
488
+ Nandi 2.0+ supports managing migrations for multiple databases within a single Rails application.
489
+
490
+ **Note:** Single database configurations continue to work without any changes. Multi-database support is fully backward compatible.
491
+
492
+ ### Configuring Multiple Databases
493
+
494
+ Instead of setting configuration values directly, register each database with its own configuration. If no values are specified, the existing defaults will be used.
495
+
496
+ **Database-specific options** (passed to `register_database`):
497
+ These options can be set individually for each database. **All are optional** - if not specified, the standard Nandi defaults are used:
498
+
499
+ - `migration_directory`: Where Nandi migrations are stored (default: `"db/safe_migrations"` for primary, `"db/<name>_safe_migrations"` for others)
500
+ - `output_directory`: Where compiled ActiveRecord migrations go (default: `"db/migrate"` for primary, `"db/<name>_migrate"` for others)
501
+ - `lockfile_name`: Name of the lockfile for this database (default: `".nandilock.yml"` for primary, `".<name>_nandilock.yml"` for others)
502
+ - `default`: Mark this database as the default when not named `:primary` (default: `false`)
503
+ - `access_exclusive_lock_timeout`: Timeout for ACCESS EXCLUSIVE locks (default: 5,000ms)
504
+ - `access_exclusive_statement_timeout`: Statement timeout for ACCESS EXCLUSIVE operations (default: 1,500ms)
505
+ - `access_exclusive_lock_timeout_limit`: Maximum allowed lock timeout (default: 5,000ms)
506
+ - `access_exclusive_statement_timeout_limit`: Maximum allowed statement timeout (default: 1,500ms)
507
+ - `concurrent_lock_timeout_limit`: Minimum timeout for concurrent operations (default: 3,600,000ms / 1 hour)
508
+ - `concurrent_statement_timeout_limit`: Minimum statement timeout for concurrent operations (default: 3,600,000ms / 1 hour)
509
+
510
+ **Global options** (set via config accessors):
511
+
512
+ These options apply to all databases:
513
+
514
+ - `config.lockfile_directory`: Directory where all lockfiles are stored (default: `"db"`)
515
+ - `config.compile_files`: Filter for which files to compile (default: `"all"`)
516
+ - `config.renderer`: Rendering backend (default: `Nandi::Renderers::ActiveRecord`)
517
+ - `config.post_process { |migration| ... }`: Optional post-processing block for formatting
518
+
519
+ ```rb
520
+ # Minimal configuration - primary uses all defaults
521
+ Nandi.configure do |config|
522
+ config.register_database(:primary) # Uses all default paths and settings
523
+
524
+ config.register_database(:analytics)
525
+
526
+ # Global options (apply to all databases)
527
+ config.lockfile_directory = "db"
528
+ config.compile_files = "all"
529
+ end
530
+
531
+ # Full example with both database-specific and global options
532
+ Nandi.configure do |config|
533
+ # Primary database (automatically becomes default)
534
+ # If no values are specified, uses the standard defaults:
535
+ # - migration_directory: "db/safe_migrations"
536
+ # - output_directory: "db/migrate"
537
+ # - lockfile_name: ".nandilock.yml"
538
+ # - All timeout values use their defaults
539
+ config.register_database(:primary,
540
+ access_exclusive_lock_timeout: 5_000 # Only override what you need
541
+ )
542
+
543
+ # Analytics database with custom paths and relaxed timeouts
544
+ config.register_database(:analytics,
545
+ migration_directory: "db/analytics_safe_migrations",
546
+ output_directory: "db/analytics_migrate",
547
+ lockfile_name: ".analytics_nandilock.yml",
548
+ access_exclusive_lock_timeout: 30_000,
549
+ access_exclusive_statement_timeout: 10_000,
550
+ concurrent_statement_timeout_limit: 7_200_000
551
+ )
552
+
553
+ # Global configuration options (apply to all databases)
554
+ config.lockfile_directory = "db" # Where all lockfiles are stored
555
+ config.compile_files = "all" # Filter for compilation
556
+ config.renderer = Nandi::Renderers::ActiveRecord # Optional, this is the default
557
+
558
+ # Optional post-processing for all compiled migrations
559
+ config.post_process do |migration|
560
+ # Format, lint, etc.
561
+ migration
562
+ end
563
+ end
564
+ ```
565
+
566
+ ### Directory Structure
567
+
568
+ Each database maintains its own directory structure. The primary database uses the default paths if not specified:
569
+
570
+ ```
571
+ db/
572
+ ├── safe_migrations/ # Primary database (default path)
573
+ │ └── 20250901_add_users.rb
574
+ ├── migrate/ # Primary database (default path)
575
+ │ └── 20250901_add_users.rb
576
+ ├── .nandilock.yml # Primary database (default)
577
+
578
+ ├── analytics_safe_migrations/ # Analytics database
579
+ │ └── 20250902_add_events.rb
580
+ ├── analytics_migrate/
581
+ │ └── 20250902_add_events.rb
582
+ ├── .analytics_nandilock.yml
583
+
584
+ ├── reporting_safe_migrations/ # Reporting database
585
+ │ └── 20250903_add_reports.rb
586
+ ├── reporting_migrate/
587
+ │ └── 20250903_add_reports.rb
588
+ └── .reporting_nandilock.yml
589
+ ```
590
+
591
+ ### Using Generators with Multiple Databases
592
+
593
+ All Nandi generators accept a `--database` option to specify which database to target:
594
+
595
+ ```bash
596
+ # Generate for primary database (default)
597
+ rails generate nandi:migration create_users_table
598
+
599
+ # Generate for analytics database
600
+ rails generate nandi:migration create_events_table --database=analytics
601
+ ```
602
+
603
+ ### Compiling Migrations
604
+
605
+ The compile generator can compile all databases or a specific one:
606
+
607
+ ```bash
608
+ # Compile all databases
609
+ rails generate nandi:compile
610
+
611
+ # Compile specific database with filter
612
+ rails generate nandi:compile --database=analytics --files=git-diff
613
+ ```
614
+
615
+ ### Default Database
616
+
617
+ - If you register a database named `:primary`, it automatically becomes the default per rails conventions
618
+ - Otherwise, mark a database as default with `default: true`
619
+ - Generators without `--database` option use the default database
620
+ - Single database configurations work without changes
621
+
473
622
  ## `.nandiignore`
474
623
 
475
624
  To protect people from writing unsafe migrations, we provide a script [`nandi-enforce`](https://github.com/gocardless/nandi/blob/master/exe/nandi-enforce) that ensures all migrations in the specified directories are safe migrations generated by Nandi.
@@ -9,6 +9,8 @@ Example:
9
9
  db/safe_migrations/20190424123727_add_check_constraint_baz_or_quux_not_null_on_foos.rb
10
10
  db/safe_migrations/20190424123728_validate_check_constraint_baz_or_quux_not_null_on_foos.rb
11
11
 
12
+ If no --database option is specified, uses the default database.
13
+
12
14
  Example:
13
15
  rails generate nandi:check_constraint foos baz_or_quux_not_null --validation-timeout 20000
14
16
 
@@ -17,3 +19,10 @@ Example:
17
19
  db/safe_migrations/20190424123728_validate_check_constraint_baz_or_quux_not_null_on_foos.rb
18
20
 
19
21
  The statement timeout in the second migration will be set to 20,000ms.
22
+
23
+ Multi-database examples:
24
+ rails generate nandi:check_constraint users email_valid --database primary
25
+ creates check constraint migrations in primary database directory
26
+
27
+ rails generate nandi:check_constraint events status_valid --database analytics
28
+ creates check constraint migrations in analytics database directory
@@ -2,10 +2,12 @@
2
2
 
3
3
  require "rails/generators"
4
4
  require "nandi/formatting"
5
+ require "nandi/multi_db_generator"
5
6
 
6
7
  module Nandi
7
8
  class CheckConstraintGenerator < Rails::Generators::Base
8
9
  include Nandi::Formatting
10
+ include Nandi::MultiDbGenerator
9
11
 
10
12
  argument :table, type: :string
11
13
  argument :name, type: :string
@@ -41,10 +43,6 @@ module Nandi
41
43
 
42
44
  private
43
45
 
44
- def base_path
45
- Nandi.config.migration_directory || "db/safe_migrations"
46
- end
47
-
48
46
  def timestamp(offset = 0)
49
47
  (Time.now.utc + offset).strftime("%Y%m%d%H%M%S")
50
48
  end
@@ -1,10 +1,15 @@
1
1
  Description:
2
- Takes all files from the db/nandi directory and turns
3
- them into ActiveRecordmigration files.
2
+ Takes all files from the safe migration directories and turns
3
+ them into ActiveRecord migration files. Supports both single
4
+ and multi-database configurations.
5
+
6
+ If no --database option is specified, compiles for all configured databases
7
+ (or the default database in single-database configurations).
4
8
 
5
9
  Examples:
6
10
  rails generate nandi:compile
7
11
  compiles all migrations that have changed since last git commit
12
+ (for all configured databases in multi-database setups)
8
13
 
9
14
  rails generate nandi:compile --files all
10
15
  compiles all migrations ever
@@ -17,3 +22,10 @@ Examples:
17
22
 
18
23
  rails generate nandi:compile --files >=20190101
19
24
  compiles all migrations from January 1st 2019 and after
25
+
26
+ Multi-database examples:
27
+ rails generate nandi:compile --database primary
28
+ compiles migrations only for the primary database
29
+
30
+ rails generate nandi:compile --database analytics
31
+ compiles migrations only for the analytics database
@@ -10,6 +10,12 @@ module Nandi
10
10
  class CompileGenerator < Rails::Generators::Base
11
11
  source_root File.expand_path("templates", __dir__)
12
12
 
13
+ class_option :database,
14
+ type: :string,
15
+ default: nil,
16
+ desc: "Database to compile. " \
17
+ "If not specified, compiles for all databases"
18
+
13
19
  class_option :files,
14
20
  type: :string,
15
21
  default: Nandi.config.compile_files,
@@ -22,41 +28,38 @@ module Nandi
22
28
  DESC
23
29
 
24
30
  def compile_migration_files
25
- Nandi.compile(files: files) do |results|
26
- results.each do |result|
27
- Nandi::Lockfile.add(
28
- file_name: result.file_name,
29
- source_digest: result.source_digest,
30
- compiled_digest: result.compiled_digest,
31
- )
32
-
33
- unless result.migration_unchanged?
34
- create_file result.output_path, result.body, force: true
31
+ databases.each do |db_name|
32
+ Nandi.compile(files: files(db_name), db_name: db_name) do |results|
33
+ results.each do |result|
34
+ Nandi::Lockfile.for(db_name).add(
35
+ file_name: result.file_name,
36
+ source_digest: result.source_digest,
37
+ compiled_digest: result.compiled_digest,
38
+ )
39
+ unless result.migration_unchanged?
40
+ create_file result.output_path, result.body, force: true
41
+ end
35
42
  end
36
43
  end
44
+ Nandi::Lockfile.for(db_name).persist!
37
45
  end
38
-
39
- Nandi::Lockfile.persist!
40
46
  end
41
47
 
42
48
  private
43
49
 
44
- def safe_migrations_dir
45
- if Nandi.config.migration_directory.nil?
46
- Rails.root.join("db", "safe_migrations").to_s
47
- else
48
- File.expand_path(Nandi.config.migration_directory)
49
- end
50
+ def databases
51
+ return [options[:database].to_sym] if options[:database]
52
+
53
+ Nandi.config.databases.names
50
54
  end
51
55
 
52
- def output_path
53
- Nandi.config.output_directory || "db/migrate"
56
+ def safe_migrations_dir(db_name)
57
+ File.expand_path(Nandi.config.migration_directory(db_name))
54
58
  end
55
59
 
56
- def files
57
- safe_migration_files = Dir.chdir(safe_migrations_dir) { Dir["*.rb"] }
58
- FileMatcher.call(files: safe_migration_files, spec: options["files"]).
59
- map { |file| File.join(safe_migrations_dir, file) }
60
+ def files(db_name)
61
+ safe_migration_files = Dir.chdir(safe_migrations_dir(db_name)) { Dir["*.rb"] }
62
+ FileMatcher.call(files: safe_migration_files, spec: options["files"])
60
63
  end
61
64
  end
62
65
  end
@@ -14,6 +14,8 @@ Example:
14
14
  new foreign key constraint on that column against the id column of bars, and
15
15
  validate that constraint separately.
16
16
 
17
+ If no --database option is specified, uses the default database.
18
+
17
19
  Example:
18
20
  rails generate nandi:foreign_key foos bars --type text
19
21
 
@@ -45,3 +47,10 @@ Example:
45
47
 
46
48
  Assumes that there is a column on table foos called special_bar_id that points to
47
49
  bars. Will create an FK constraint called my_fk
50
+
51
+ Multi-database examples:
52
+ rails generate nandi:foreign_key posts users --database primary
53
+ creates foreign key migrations in primary database directory
54
+
55
+ rails generate nandi:foreign_key events sessions --database analytics
56
+ creates foreign key migrations in analytics database directory
@@ -2,10 +2,12 @@
2
2
 
3
3
  require "rails/generators"
4
4
  require "nandi/formatting"
5
+ require "nandi/multi_db_generator"
5
6
 
6
7
  module Nandi
7
8
  class ForeignKeyGenerator < Rails::Generators::Base
8
9
  include Nandi::Formatting
10
+ include Nandi::MultiDbGenerator
9
11
 
10
12
  argument :table, type: :string
11
13
  argument :target, type: :string
@@ -68,10 +70,6 @@ module Nandi
68
70
  :"#{target.singularize}_id"
69
71
  end
70
72
 
71
- def base_path
72
- Nandi.config.migration_directory || "db/safe_migrations"
73
- end
74
-
75
73
  def timestamp(offset = 0)
76
74
  (Time.now.utc + offset).strftime("%Y%m%d%H%M%S")
77
75
  end
@@ -1,6 +1,8 @@
1
1
  Description:
2
2
  Generates new database indices
3
3
 
4
+ If no --database option is specified, uses the default database.
5
+
4
6
  Example:
5
7
  rails generate nandi:index table_name index_col
6
8
 
@@ -29,3 +31,10 @@ Example:
29
31
  This will create:
30
32
  db/safe_migrations/20190424123727_add_index_on_index_col_to_table_name.rb
31
33
  with the specified bespoke index name
34
+
35
+ Multi-database examples:
36
+ rails generate nandi:index users email --database primary
37
+ creates index migration in primary database directory
38
+
39
+ rails generate nandi:index events created_at --database analytics
40
+ creates index migration in analytics database directory
@@ -2,10 +2,12 @@
2
2
 
3
3
  require "rails/generators"
4
4
  require "nandi/formatting"
5
+ require "nandi/multi_db_generator"
5
6
 
6
7
  module Nandi
7
8
  class IndexGenerator < Rails::Generators::Base
8
9
  include Nandi::Formatting
10
+ include Nandi::MultiDbGenerator
9
11
 
10
12
  argument :tables, type: :string
11
13
  argument :columns, type: :string
@@ -42,10 +44,6 @@ module Nandi
42
44
  (Time.now.utc + offset).strftime("%Y%m%d%H%M%S")
43
45
  end
44
46
 
45
- def base_path
46
- Nandi.config.migration_directory || "db/safe_migrations"
47
- end
48
-
49
47
  def override_index_name
50
48
  options["index_name"]&.to_sym
51
49
  end
@@ -1,9 +1,19 @@
1
1
  Description:
2
2
  Generates a new database migration which uses the
3
- nandi DSL in db/nandi
3
+ nandi DSL. Supports both single and multi-database
4
+ configurations.
4
5
 
5
- Example:
6
+ If no --database option is specified, uses the default database.
7
+
8
+ Examples:
6
9
  rails generate nandi:migration add_stuff_to_things
7
10
 
8
11
  This will create:
9
12
  db/safe_migrations/20190424123727_add_stuff_to_things.rb
13
+
14
+ Multi-database examples:
15
+ rails generate nandi:migration add_stuff_to_things --database primary
16
+ creates migration in primary database directory
17
+
18
+ rails generate nandi:migration add_analytics_table --database analytics
19
+ creates migration in analytics database directory
@@ -1,9 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "rails/generators"
4
+ require "nandi/multi_db_generator"
4
5
 
5
6
  module Nandi
6
7
  class MigrationGenerator < Rails::Generators::NamedBase
8
+ include Nandi::MultiDbGenerator
9
+
7
10
  source_root File.expand_path("templates", __dir__)
8
11
 
9
12
  def create_migration_file
@@ -14,11 +17,5 @@ module Nandi
14
17
  "#{base_path}/#{timestamp}_#{file_name.underscore}.rb",
15
18
  )
16
19
  end
17
-
18
- private
19
-
20
- def base_path
21
- Nandi.config.migration_directory || "db/safe_migrations"
22
- end
23
20
  end
24
21
  end
@@ -2,6 +2,8 @@ Description:
2
2
  Generates two new database migrations which will safely add a check that
3
3
  a column is not null, and validate it separately.
4
4
 
5
+ If no --database option is specified, uses the default database.
6
+
5
7
  Example:
6
8
  rails generate nandi:not_null_check foos bar
7
9
 
@@ -17,3 +19,10 @@ Example:
17
19
  db/safe_migrations/20190424123728_validate_not_null_check_on_bar_to_foos.rb
18
20
 
19
21
  The statement timeout in the second migration will be set to 20,000ms.
22
+
23
+ Multi-database examples:
24
+ rails generate nandi:not_null_check users email --database primary
25
+ creates not null check migrations in primary database directory
26
+
27
+ rails generate nandi:not_null_check events user_id --database analytics
28
+ creates not null check migrations in analytics database directory
@@ -2,10 +2,12 @@
2
2
 
3
3
  require "rails/generators"
4
4
  require "nandi/formatting"
5
+ require "nandi/multi_db_generator"
5
6
 
6
7
  module Nandi
7
8
  class NotNullCheckGenerator < Rails::Generators::Base
8
9
  include Nandi::Formatting
10
+ include Nandi::MultiDbGenerator
9
11
 
10
12
  argument :table, type: :string
11
13
  argument :column, type: :string
@@ -41,10 +43,6 @@ module Nandi
41
43
 
42
44
  private
43
45
 
44
- def base_path
45
- Nandi.config.migration_directory || "db/safe_migrations"
46
- end
47
-
48
46
  def timestamp(offset = 0)
49
47
  (Time.now.utc + offset).strftime("%Y%m%d%H%M%S")
50
48
  end
@@ -6,15 +6,21 @@ module Nandi
6
6
  class CompiledMigration
7
7
  class InvalidMigrationError < StandardError; end
8
8
 
9
- attr_reader :file_name, :source_file_path, :class_name
9
+ attr_reader :file_name, :source_file_path, :class_name, :db_config
10
10
 
11
- def self.build(source_file_path)
12
- new(source_file_path)
11
+ def db_name
12
+ @db_config.name
13
13
  end
14
14
 
15
- def initialize(source_file_path)
16
- @source_file_path = source_file_path
17
- require source_file_path
15
+ def self.build(file_name:, db_name:)
16
+ new(file_name:, db_name:)
17
+ end
18
+
19
+ def initialize(file_name:, db_name:)
20
+ @file_name = file_name
21
+ @db_config = Nandi.config.database(db_name)
22
+ @source_file_path = File.join(@db_config.migration_directory, file_name)
23
+ require File.expand_path(source_file_path)
18
24
 
19
25
  @file_name, @class_name = /\d+_([a-z0-9_]+)\.rb\z/.match(source_file_path)[0..1]
20
26
  end
@@ -40,7 +46,7 @@ module Nandi
40
46
  end
41
47
 
42
48
  def output_path
43
- "#{Nandi.compiled_output_directory}/#{file_name}"
49
+ "#{db_config.output_directory}/#{file_name}"
44
50
  end
45
51
 
46
52
  def migration
@@ -58,14 +64,16 @@ module Nandi
58
64
  def migration_unchanged?
59
65
  return false unless File.exist?(output_path)
60
66
 
67
+ lockfile = Nandi::Lockfile.for(db_config.name)
68
+
61
69
  source_migration_diff = Nandi::FileDiff.new(
62
70
  file_path: source_file_path,
63
- known_digest: Nandi::Lockfile.get(file_name).fetch(:source_digest),
71
+ known_digest: lockfile.get(file_name).fetch(:source_digest),
64
72
  )
65
73
 
66
74
  compiled_migration_diff = Nandi::FileDiff.new(
67
75
  file_path: output_path,
68
- known_digest: Nandi::Lockfile.get(file_name).fetch(:compiled_digest),
76
+ known_digest: lockfile.get(file_name).fetch(:compiled_digest),
69
77
  )
70
78
 
71
79
  source_migration_diff.unchanged? && compiled_migration_diff.unchanged?