Skip to content

Instantly share code, notes, and snippets.

@anujbiyani
Last active April 16, 2024 23:04
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anujbiyani/25f3d3096fe76f91fe271b2a722b4cc0 to your computer and use it in GitHub Desktop.
Save anujbiyani/25f3d3096fe76f91fe271b2a722b4cc0 to your computer and use it in GitHub Desktop.
A script to make sure new Rails migrations cleanly migrate forwards and backwards.
#!/usr/bin/env ruby
require "pathname"
common_ancestor_sha = `git merge-base HEAD origin/master`.strip
commit_messages = `git log --pretty=format:"%B" #{common_ancestor_sha}..HEAD`
if commit_messages.include?("[skip schema checks]")
puts "Found [skip schema checks] tag, skipping db migration check."
exit
end
changed_files = `git diff --name-only HEAD #{common_ancestor_sha}`.split("\n")
migration_files = changed_files.select { |path| File.dirname(path) == "db/migrate" }
schema_file = changed_files.detect { |path| path.include?("db/schema.rb") }
if schema_file && migration_files.none?
puts "DB schema file changed, but no migration files are present. All changes to the schema should be driven by a migration file."
puts "If you really mean to change the schema without a migration, e.g. you are fixing a bad schema, then please disable this check in this PR and re-enable it after this PR is merged."
puts "Failing."
exit 1
end
unless migration_files.any?
puts "Did not find any migration files in the list of files changed. Exiting."
exit
end
versions = migration_files.map { |path| Pathname.new(path).basename.to_s.split("_").first }.sort
human_formatted_versions = versions.join(", ")
puts "Testing migrations <#{human_formatted_versions}>"
puts "First step: rolling back each migration and checking the DB schema file."
versions.reverse_each do |version|
success = system("bundle exec rake db:migrate:down VERSION=#{version}")
unless success
puts "When rolling back the migration <#{version}>, the command failed."
exit 1
end
end
diff = `git diff #{common_ancestor_sha} -- db/schema.rb`
if diff.empty?
puts "Rolling back migrations resulted in the same DB schema file as master."
else
raise <<~MSG
When rolling back the migrations <#{human_formatted_versions}>, the DB schema file did not match master. We observed the following differences:
#{diff}
Please ensure reversing your migrations results in the same db/schema.rb file as master.
MSG
end
puts "Last step: running each migration forwards, and checking the DB schema file and missing annotations."
versions.each do |version|
success = system("bundle exec rake MISC_TEST=true db:migrate:up VERSION=#{version}")
unless success
puts "When running migration <#{version}> forward, the command failed."
exit 1
end
end
if `git status --porcelain`.empty?
puts "Running migrations forwards resulted in no changes, so your DB schema file is stable and annotations have been checked-in."
else
raise <<~MSG
Running migrations forwards resulted in the following changes:
#{`git diff`}
Either your DB schema file is not stable (aka if you were to merge this branch to master and another dev pulled your changes and ran migrations, their schema file would differ) or you have not checked in annotations.
MSG
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment