lib/hanami/cli/commands/app/db/command.rb in hanami-cli-2.2.0.beta2 vs lib/hanami/cli/commands/app/db/command.rb in hanami-cli-2.2.0.rc1
- old
+ new
@@ -15,30 +15,44 @@
# @api private
class Command < App::Command
option :app, required: false, type: :flag, default: false, desc: "Use app database"
option :slice, required: false, desc: "Use database for slice"
+ # @api private
attr_reader :system_call
+ # @api private
+ attr_reader :test_env_executor
+
def initialize(
out:, err:,
system_call: SystemCall.new,
+ test_env_executor: InteractiveSystemCall.new(out: out, err: err),
+ nested_command: false,
**opts
)
super(out: out, err: err, **opts)
@system_call = system_call
+ @test_env_executor = test_env_executor
+ @nested_command = nested_command
end
def run_command(klass, ...)
klass.new(
out: out,
inflector: inflector,
fs: fs,
system_call: system_call,
+ test_env_executor: test_env_executor,
+ nested_command: true,
).call(...)
end
+ def nested_command?
+ @nested_command
+ end
+
private
def databases(app: false, slice: nil, gateway: nil)
if gateway && !app && !slice
err.puts "When specifying --gateway, an --app or --slice must also be given"
@@ -75,11 +89,11 @@
else
databases.values
end
end
- def all_databases # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
+ def all_databases # rubocop:disable Metrics/AbcSize
slices = [app] + app.slices.with_nested
slice_gateways_by_database_url = slices.each_with_object({}) { |slice, hsh|
db_provider_source = slice.container.providers[:db]&.source
next unless db_provider_source
@@ -90,21 +104,23 @@
end
}
slice_gateways_by_database_url.each_with_object([]) { |(url, slice_gateways), arr|
slice_gateways_with_config = slice_gateways.select {
- _1[:slice].root.join("config", "db").directory?
+ migrate_dir = _1[:gateway] == :default ? "migrate" : "#{_1[:gateway]}_migrate"
+
+ _1[:slice].root.join("config", "db", migrate_dir).directory?
}
db_slice_gateway = slice_gateways_with_config.first || slice_gateways.first
database = Utils::Database.database_class(url).new(
slice: db_slice_gateway.fetch(:slice),
gateway_name: db_slice_gateway.fetch(:gateway),
system_call: system_call
)
- warn_on_misconfigured_database database, slice_gateways.map { _1.fetch(:slice) }
+ warn_on_misconfigured_database database, slice_gateways_with_config.map { _1.fetch(:slice) }
arr << database
}
end
@@ -124,11 +140,11 @@
out.puts <<~STR
WARNING: Database #{database.name} is configured for multiple config/db/ directories:
#{slices.map { "- " + _1.root.relative_path_from(_1.app.root).join("config", "db").to_s }.join("\n")}
- Migrating database using #{database.slice.slice_name.to_s.inspect} slice only.
+ Using config in #{database.slice.slice_name.to_s.inspect} slice only.
STR
elsif !database.db_config_dir?
relative_path = database.slice.root
.relative_path_from(database.slice.app.root)
@@ -137,9 +153,69 @@
out.puts <<~STR
WARNING: Database #{database.name} expects the folder #{relative_path}/ to exist but it does not.
STR
end
+ end
+
+ # Invokes the currently executing `hanami` CLI command again, but with any `--env` args
+ # removed and the `HANAMI_ENV=test` env var set.
+ #
+ # This is called by certain `db` commands only, and runs only if the Hanami env is
+ # `:development`. This behavior important to streamline the local development
+ # experience, making sure that the test databases are kept in sync with operations run
+ # on the development databases.
+ #
+ # Spawning an entirely new process to change the env is a compromise approach until we
+ # can have an API for reinitializing the DB subsystem in-process with a different env.
+ def re_run_development_command_in_test
+ # Only invoke a new process if we've been called as `hanami`. This avoids awkward
+ # failures when testing commands via RSpec, for which the $0 is "/full/path/to/rspec".
+ return unless $0.end_with?("hanami")
+
+ # If this special env key is set, then a re-run has already been invoked. This would
+ # mean the current command is actually a nested command run by another db command. In
+ # this case, don't trigger a re-runs, because one is already in process.
+ return if nested_command?
+
+ # Re-runs in test are for development-env commands only.
+ return unless Hanami.env == :development
+
+ cmd = $0
+ cmd = "bundle exec #{cmd}" if ENV.key?("BUNDLE_BIN_PATH")
+
+ test_env_executor.call(
+ cmd, *argv_without_env_args,
+ env: {
+ "HANAMI_ENV" => "test",
+ "HANAMI_CLI_DB_COMMAND_RE_RUN_IN_TEST" => "true"
+ }
+ )
+ end
+
+ def re_running_in_test?
+ ENV.key?("HANAMI_CLI_DB_COMMAND_RE_RUN_IN_TEST")
+ end
+
+ # Returns the `ARGV` with every option argument included, but the `-e` or `--env` args
+ # removed.
+ def argv_without_env_args
+ new_argv = ARGV.dup
+
+ env_arg_index = new_argv.index {
+ _1 == "-e" || _1 == "--env" || _1.start_with?("-e=") || _1.start_with?("--env=")
+ }
+
+ if env_arg_index
+ # Remove the env argument
+ env_arg = new_argv.delete_at(env_arg_index)
+
+ # If the env argument is not in combined form ("--env foo" rather than "--env=foo"),
+ # then remove the following argument too
+ new_argv.delete_at(env_arg_index) if ["-e", "--env"].include?(env_arg)
+ end
+
+ new_argv
end
end
end
end
end