summaryrefslogtreecommitdiff
path: root/scripts/regenerate-schema
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/regenerate-schema')
-rwxr-xr-xscripts/regenerate-schema182
1 files changed, 182 insertions, 0 deletions
diff --git a/scripts/regenerate-schema b/scripts/regenerate-schema
new file mode 100755
index 00000000000..cedd612f766
--- /dev/null
+++ b/scripts/regenerate-schema
@@ -0,0 +1,182 @@
+#!/usr/bin/env ruby
+
+# frozen_string_literal: true
+
+require 'open3'
+require 'uri'
+
+class SchemaRegenerator
+ ##
+ # Filename of the schema
+ #
+ # This file is being regenerated by this script.
+ FILENAME = 'db/structure.sql'
+
+ ##
+ # Directories where migrations are stored
+ #
+ # The methods +hide_migrations+ and +unhide_migrations+ will rename
+ # these to disable/enable migrations.
+ MIGRATION_DIRS = %w[db/migrate db/post_migrate].freeze
+
+ def execute
+ Dir.chdir(File.expand_path('..', __dir__)) do
+ checkout_ref
+ checkout_clean_schema
+ hide_migrations
+ reset_db
+ unhide_migrations
+ migrate
+ ensure
+ unhide_migrations
+ end
+ end
+
+ private
+
+ ##
+ # Git checkout +CI_COMMIT_SHA+.
+ #
+ # When running from CI, checkout the clean commit,
+ # not the merged result.
+ def checkout_ref
+ return unless ci?
+
+ run %Q[git checkout #{source_ref}]
+ run %q[git clean -f -- db]
+ end
+
+ ##
+ # Checkout the clean schema from the target branch
+ def checkout_clean_schema
+ remote_checkout_clean_schema || local_checkout_clean_schema
+ end
+
+ ##
+ # Get clean schema from remote servers
+ #
+ # This script might run in CI, using a shallow clone, so to checkout
+ # the file, fetch the target branch from the server.
+ def remote_checkout_clean_schema
+ return false unless project_url
+ return false unless target_project_url
+
+ run %Q[git remote add target_project #{target_project_url}.git]
+ run %Q[git fetch target_project #{target_branch}:#{target_branch}]
+
+ local_checkout_clean_schema
+ end
+
+ ##
+ # Git checkout the schema from target branch.
+ #
+ # Ask git to checkout the schema from the target branch and reset
+ # the file to unstage the changes.
+ def local_checkout_clean_schema
+ run %Q[git checkout #{merge_base} -- #{FILENAME}]
+ run %Q[git reset -- #{FILENAME}]
+ end
+
+ ##
+ # Move migrations to where Rails will not find them.
+ #
+ # To reset the database to clean schema defined in +FILENAME+, move
+ # the migrations to a path where Rails will not find them, otherwise
+ # +db:reset+ would abort. Later when the migrations should be
+ # applied, use +unhide_migrations+ to bring them back.
+ def hide_migrations
+ MIGRATION_DIRS.each do |dir|
+ File.rename(dir, "#{dir}__")
+ end
+ end
+
+ ##
+ # Undo the effect of +hide_migrations+.
+ #
+ # Place back the migrations which might be moved by
+ # +hide_migrations+.
+ def unhide_migrations
+ error = nil
+
+ MIGRATION_DIRS.each do |dir|
+ File.rename("#{dir}__", dir)
+ rescue Errno::ENOENT
+ nil
+ rescue StandardError => e
+ # Save error for later, but continue with other dirs first
+ error = e
+ end
+
+ raise error if error
+ end
+
+ ##
+ # Run rake task to reset the database.
+ def reset_db
+ run %q[bin/rails db:reset RAILS_ENV=test]
+ end
+
+ ##
+ # Run rake task to run migrations.
+ def migrate
+ run %q[bin/rails db:migrate RAILS_ENV=test]
+ end
+
+ ##
+ # Run the given +cmd+.
+ #
+ # The command is colored green, and the output of the command is
+ # colored gray.
+ # When the command failed an exception is raised.
+ def run(cmd)
+ puts "\e[32m$ #{cmd}\e[37m"
+ stdout_str, stderr_str, status = Open3.capture3(cmd)
+ puts "#{stdout_str}#{stderr_str}\e[0m"
+ raise("Command failed: #{stderr_str}") unless status.success?
+
+ stdout_str
+ end
+
+ ##
+ # Return the base commit between source and target branch.
+ def merge_base
+ @merge_base ||= run("git merge-base #{target_branch} #{source_ref}").chomp
+ end
+
+ ##
+ # Return the name of the target branch
+ #
+ # Get source ref from CI environment variable, or read the +TARGET+
+ # environment+ variable, or default to +HEAD+.
+ def target_branch
+ ENV['CI_MERGE_REQUEST_TARGET_BRANCH_NAME'] || ENV['TARGET'] || 'master'
+ end
+
+ ##
+ # Return the source ref
+ #
+ # Get source ref from CI environment variable, or default to +HEAD+.
+ def source_ref
+ ENV['CI_COMMIT_SHA'] || 'HEAD'
+ end
+
+ ##
+ # Return the source project URL from CI environment variable.
+ def project_url
+ ENV['CI_PROJECT_URL']
+ end
+
+ ##
+ # Return the target project URL from CI environment variable.
+ def target_project_url
+ ENV['CI_MERGE_REQUEST_PROJECT_URL']
+ end
+
+ ##
+ # Return whether the script is running from CI
+ def ci?
+ ENV['CI']
+ end
+end
+
+SchemaRegenerator.new.execute