DEV: Introduce script/promote_migrations tool

Post-deploy migrations exist to allow for seamless Discourse upgrades. By design, they cause migrations to run out of numerical order. This has the potential to cause some unexpected edge cases. To reduce the likelihood of these edge cases, we will promote historical post_deploy migrations to regular migrations after a full Discourse stable release cycle.

This script is intended to be run at least during every Discourse release cycle.

This means that truly seamless upgrades will not be possible between non-consecutive Discourse versions. (Upgrades will still work, but may cause some server errors for users during the upgrade)
This commit is contained in:
David Taylor 2021-06-23 13:31:58 +01:00
parent d2c5165052
commit 49f39434c4

97
script/promote_migrations Executable file
View File

@ -0,0 +1,97 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
# This script will promote post_migrate files
# which have existed for more than one Discourse
# stable version cycle.
#
# Renames will be staged in git, but not committed
#
# Usage:
# script/promote_migrations [--dry-run] [--plugins]
require 'open3'
require 'fileutils'
VERSION_REGEX = %r{\/(\d+)_}
DRY_RUN = ARGV.include? '--dry-run'
PLUGINS = ARGV.include? '--plugins'
def run(*args, capture: true)
out, s = Open3.capture2(*args)
if s.exitstatus != 0
STDERR.puts "Command failed: '#{args.join(' ')}'"
exit 1
end
out.strip
end
current_version = run 'git describe --abbrev=0 --match "v*"'
puts "Current version is #{current_version}"
run 'git fetch'
current_stable_version =
run 'git describe --abbrev=0 --match "v*" origin/stable'
puts "Current stable version is #{current_stable_version}"
minor = current_stable_version[/^(v\d+\.\d+)\./, 1]
previous_stable_version =
run "git describe --abbrev=0 --match 'v*' --exclude '#{minor}*' origin/stable"
puts "Previous stable version is #{previous_stable_version}"
stable_post_migrate_filenames =
run(
'git',
'ls-tree',
'--name-only',
'-r',
previous_stable_version,
'db/post_migrate'
).split("\n")
stable_post_migrate_filenames.sort!
latest_stable_post_migration = stable_post_migrate_filenames.last
puts "The latest core post_migrate file in #{previous_stable_version} is #{latest_stable_post_migration}"
puts 'Promoting this, and all earlier post_migrates, to regular migrations'
promote_threshold = latest_stable_post_migration[VERSION_REGEX, 1].to_i
current_post_migrations =
if PLUGINS
puts 'Looking in plugins...'
Dir.glob('plugins/*/db/post_migrate/*')
else
Dir.glob('db/post_migrate/*')
end
if current_post_migrations.length == 0
puts 'No post_migrate files found. All done'
end
current_post_migrations.each do |path|
version = path[VERSION_REGEX, 1].to_i
file = File.basename(path)
dir = File.dirname(path)
if version <= promote_threshold
print "Promoting #{path}..."
if DRY_RUN
puts ' (dry run)'
else
run 'mkdir', '-p', "#{dir}/../migrate"
run 'git', '-C', dir, 'mv', file, "../migrate/#{file}"
puts ' (done)'
end
end
end
puts 'Done! File moves are staged and ready for commit.'
puts 'Suggested commit message:'
puts '-' * 20
puts <<~MESSAGE
DEV: Promote historic post_deploy migrations
This commit promotes all post_deploy migrations which existed in Discourse #{previous_stable_version} (timestamp <= #{promote_threshold})
MESSAGE
puts '-' * 20