183 lines
4.2 KiB
Text
183 lines
4.2 KiB
Text
|
#!/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
|