FIX: migrations-tooling CLI didn't work anymore (#29777)

The previous approach of splitting Thor commands into multiple files caused problems when the same method name was used in multiple commands.

This also loads the Rails environment only for commands that need it. That makes the CLI boot faster for most commands or when the help should be shown. That's also why we can't use `Rails.root` in the CLI.
This commit is contained in:
Gerhard Schlager 2024-11-19 23:51:53 +01:00 committed by GitHub
parent a8ca82b11f
commit 75f4a14568
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 114 additions and 96 deletions

View File

@ -1,23 +1,49 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
# frozen_string_literal: true # frozen_string_literal: true
require "thor"
require_relative "../lib/migrations" require_relative "../lib/migrations"
require "colored2"
require "thor"
module Migrations module Migrations
load_rails_environment
configure_zeitwerk configure_zeitwerk
enable_i18n enable_i18n
class CommandLineInterface < Thor module CLI
include ::Migrations::CLI::ConvertCommand class Application < Thor
include ::Migrations::CLI::ImportCommand desc "convert [FROM]", "Convert a file"
include ::Migrations::CLI::UploadCommand option :settings, type: :string, desc: "Path of settings file", banner: "path"
option :reset, type: :boolean, desc: "Reset database before converting data"
def convert(converter_type)
::Migrations::CLI::ConvertCommand.new(converter_type, options).execute
end
def self.exit_on_failure? desc "import", "Import a file"
true def import
::Migrations::CLI::ImportCommand.new(options).execute
end
desc "upload", "Upload media uploads"
option :settings,
type: :string,
desc: "Uploads settings file path",
default: "./migrations/config/upload.yml",
aliases: "-s",
banner: "path"
option :fix_missing, type: :boolean, desc: "Fix missing uploads"
option :optimize, type: :boolean, desc: "Optimize uploads"
def upload
::Migrations::CLI::UploadCommand.new(options).execute
end
def self.exit_on_failure?
true
end
end end
end end
Dir.chdir(Rails.root) { CommandLineInterface.start } # rubocop:disable Discourse/NoChdir
end end
# rubocop:disable Discourse/NoChdir
Dir.chdir(File.expand_path("../..", __dir__)) { ::Migrations::CLI::Application.start }
# rubocop:enable Discourse/NoChdir

View File

@ -1,52 +1,48 @@
# frozen_string_literal: true # frozen_string_literal: true
module Migrations::CLI::ConvertCommand module Migrations::CLI
def self.included(thor) class ConvertCommand
thor.class_eval do def initialize(converter_type, options)
desc "convert [FROM]", "Convert a file" @converter_type = converter_type.downcase
option :settings, type: :string, desc: "Path of settings file", banner: "path" @options = options
option :reset, type: :boolean, desc: "Reset database before converting data" end
def convert(converter_type)
converter_type = converter_type.downcase
validate_converter_type!(converter_type)
settings = load_settings(converter_type) def execute
validate_converter_type!
settings = load_settings
::Migrations::Database.reset!(settings[:intermediate_db][:path]) if options[:reset] ::Migrations::Database.reset!(settings[:intermediate_db][:path]) if @options[:reset]
converter = "migrations/converters/#{converter_type}/converter".camelize.constantize converter = "migrations/converters/#{@converter_type}/converter".camelize.constantize
converter.new(settings).run converter.new(settings).run
end end
private private
def validate_converter_type!(type) def validate_converter_type!
converter_names = ::Migrations::Converters.names converter_names = ::Migrations::Converters.names
raise Thor::Error, <<~MSG if !converter_names.include?(type) raise Thor::Error, <<~MSG if !converter_names.include?(@converter_type)
Unknown converter name: #{type} Unknown converter name: #{@converter_type}
Valid names are: #{converter_names.join(", ")} Valid names are: #{converter_names.join(", ")}
MSG MSG
end end
def validate_settings_path!(settings_path) def validate_settings_path!(settings_path)
if !File.exist?(settings_path) raise Thor::Error, "Settings file not found: #{settings_path}" if !File.exist?(settings_path)
raise Thor::Error, "Settings file not found: #{settings_path}" end
end
end
def load_settings(converter_type) def load_settings
settings_path = calculate_settings_path(converter_type) settings_path = calculate_settings_path
validate_settings_path!(settings_path) validate_settings_path!(settings_path)
YAML.safe_load(File.read(settings_path), symbolize_names: true) YAML.safe_load(File.read(settings_path), symbolize_names: true)
end end
def calculate_settings_path(converter_type) def calculate_settings_path
settings_path = settings_path =
options[:settings] || ::Migrations::Converters.default_settings_path(converter_type) @options[:settings] || ::Migrations::Converters.default_settings_path(@converter_type)
File.expand_path(settings_path, Dir.pwd) File.expand_path(settings_path, Dir.pwd)
end
end end
end end
end end

View File

@ -1,15 +1,18 @@
# frozen_string_literal: true # frozen_string_literal: true
module Migrations::CLI::ImportCommand require "extralite"
def self.included(thor)
thor.class_eval do
desc "import", "Import a file"
def import
require "extralite"
puts "Importing into Discourse #{Discourse::VERSION::STRING}" module Migrations::CLI
puts "Extralite SQLite version: #{Extralite.sqlite3_version}" class ImportCommand
end def initialize(options)
@options = options
end
def execute
::Migrations.load_rails_environment
puts "Importing into Discourse #{Discourse::VERSION::STRING}"
puts "Extralite SQLite version: #{Extralite.sqlite3_version}"
end end
end end
end end

View File

@ -1,49 +1,40 @@
# frozen_string_literal: true # frozen_string_literal: true
module Migrations::CLI::UploadCommand module Migrations::CLI
def self.included(thor) class UploadCommand
thor.class_eval do def initialize(options)
desc "upload", "Upload media uploads" @options = options
option :settings, end
type: :string,
desc: "Uploads settings file path",
default: "./migrations/config/upload.yml",
aliases: "-s",
banner: "path"
option :fix_missing, type: :boolean, desc: "Fix missing uploads"
option :optimize, type: :boolean, desc: "Optimize uploads"
def upload
puts "Starting uploads..."
validate_settings_file! def execute
settings = load_settings puts "Starting uploads..."
::Migrations::Uploader::Uploads.perform!(settings) validate_settings_file!
settings = load_settings
puts "" ::Migrations::Uploader::Uploads.perform!(settings)
end
private puts ""
end
def load_settings private
settings = ::Migrations::SettingsParser.parse!(options.settings)
merge_settings_from_cli_args!(settings)
settings def load_settings
end settings = ::Migrations::SettingsParser.parse!(@options.settings)
merge_settings_from_cli_args!(@options, settings)
def merge_settings_from_cli_args!(settings) settings
settings[:fix_missing] = options.fix_missing if options.fix_missing.present? end
settings[:create_optimized_images] = options.optimize if options.optimize.present?
end
def validate_settings_file! def merge_settings_from_cli_args!(settings)
path = options.settings settings[:fix_missing] = options.fix_missing if @options.fix_missing.present?
settings[:create_optimized_images] = options.optimize if @options.optimize.present?
end
if !File.exist?(path) def validate_settings_file!
raise ::Migrations::NoSettingsFound, "Settings file not found: #{path}" path = @options.settings
end
end raise ::Migrations::NoSettingsFound, "Settings file not found: #{path}" if !File.exist?(path)
end end
end end
end end

View File

@ -19,7 +19,7 @@ module Migrations
def self.load_rails_environment(quiet: false) def self.load_rails_environment(quiet: false)
message = "Loading Rails environment ..." message = "Loading Rails environment ..."
print message unless quiet print message if !quiet
rails_root = File.expand_path("../..", __dir__) rails_root = File.expand_path("../..", __dir__)
# rubocop:disable Discourse/NoChdir # rubocop:disable Discourse/NoChdir
@ -33,9 +33,11 @@ module Migrations
end end
# rubocop:enable Discourse/NoChdir # rubocop:enable Discourse/NoChdir
print "\r" if !quiet
print " " * message.length print "\r"
print "\r" print " " * message.length
print "\r"
end
end end
def self.configure_zeitwerk def self.configure_zeitwerk
@ -49,7 +51,7 @@ module Migrations
loader.push_dir(File.join(::Migrations.root_path, "lib"), namespace: ::Migrations) loader.push_dir(File.join(::Migrations.root_path, "lib"), namespace: ::Migrations)
loader.push_dir(File.join(::Migrations.root_path, "lib", "common"), namespace: ::Migrations) loader.push_dir(File.join(::Migrations.root_path, "lib", "common"), namespace: ::Migrations)
# All sub-directories of a converter should have the same namespace. # All subdirectories of a converter should have the same namespace.
# Unfortunately `loader.collapse` doesn't work recursively. # Unfortunately `loader.collapse` doesn't work recursively.
Converters.all.each do |name, converter_path| Converters.all.each do |name, converter_path|
module_name = name.camelize.to_sym module_name = name.camelize.to_sym