From c3346c6b246298fd96be67b9c27b5bbc659aadd0 Mon Sep 17 00:00:00 2001 From: Alessandro Fazzi Date: Tue, 4 Jan 2022 13:01:27 +0100 Subject: [PATCH 1/3] Wordmove does not require anymore various local config The only needed one is `local.wordpress_path` now. Other config are taken through - the now mandatory - wp-cli. --- .rubocop.yml | 2 +- .ruby-version | 2 +- lib/wordmove.rb | 8 +- lib/wordmove/actions/adapt_local_db.rb | 137 ++++++++++++------ lib/wordmove/actions/adapt_remote_db.rb | 96 ++++++++---- lib/wordmove/actions/backup_local_db.rb | 44 ++++-- lib/wordmove/assets/wordmove_schema_local.yml | 4 +- lib/wordmove/cli.rb | 16 +- lib/wordmove/generators/movefile.rb | 9 +- lib/wordmove/generators/movefile.yml | 14 +- lib/wordmove/generators/movefile_adapter.rb | 89 ------------ lib/wordmove/movefile.rb | 30 +++- lib/wordmove/organizers/ftp/pull.rb | 4 +- lib/wordmove/organizers/ftp/push.rb | 4 +- lib/wordmove/organizers/ssh/pull.rb | 4 +- lib/wordmove/organizers/ssh/push.rb | 4 +- lib/wordmove/wpcli.rb | 60 ++++---- spec/actions/adapt_local_db_spec.rb | 32 +++- spec/actions/backup_local_db_spec.rb | 77 ++++++++++ spec/{docotor_spec.rb => doctor_spec.rb} | 0 spec/features/movefile_spec.rb | 25 ---- spec/fixtures/movefiles/Movefile | 7 +- spec/fixtures/movefiles/custom_paths | 8 +- spec/fixtures/movefiles/multi_environments | 7 - .../multi_environments_wpcli_sql_adapter | 7 - spec/fixtures/movefiles/with_forbidden_tasks | 8 +- spec/fixtures/movefiles/with_hooks | 9 +- spec/fixtures/movefiles/with_secrets | 8 +- .../with_secrets_with_empty_local_db_password | 8 +- spec/hook_spec.rb | 38 ++--- spec/movefile_spec.rb | 9 +- spec/spec_helper.rb | 34 ++++- spec/support/action_helpers.rb | 5 +- spec/wordpress_directory_spec.rb | 4 +- spec/wpcli_spec.rb | 33 ++--- wordmove.gemspec | 2 +- 36 files changed, 460 insertions(+), 388 deletions(-) delete mode 100644 lib/wordmove/generators/movefile_adapter.rb create mode 100644 spec/actions/backup_local_db_spec.rb rename spec/{docotor_spec.rb => doctor_spec.rb} (100%) diff --git a/.rubocop.yml b/.rubocop.yml index a23763b4..23550894 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,5 +1,5 @@ AllCops: - TargetRubyVersion: 2.7.1 + TargetRubyVersion: 3.0.2 DisplayCopNames: true DisplayStyleGuide: true NewCops: enable diff --git a/.ruby-version b/.ruby-version index 860487ca..b5021469 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.7.1 +3.0.2 diff --git a/lib/wordmove.rb b/lib/wordmove.rb index aa7358f1..a3b619b0 100644 --- a/lib/wordmove.rb +++ b/lib/wordmove.rb @@ -17,6 +17,8 @@ require 'photocopier' +require 'wordmove/wpcli' + require 'wordmove/cli' require 'wordmove/doctor' require 'wordmove/doctor/movefile' @@ -32,9 +34,7 @@ require 'wordmove/wordpress_directory' require 'wordmove/version' require 'wordmove/environments_list' -require 'wordmove/wpcli' -require 'wordmove/generators/movefile_adapter' require 'wordmove/generators/movefile' require 'wordmove/db_paths_config' @@ -42,8 +42,8 @@ require 'wordmove/actions/helpers' require 'wordmove/actions/ssh/helpers' require 'wordmove/actions/ftp/helpers' -Dir[File.join(__dir__, 'wordmove/actions/**/*.rb')].sort.each { |file| require file } -Dir[File.join(__dir__, 'wordmove/organizers/**/*.rb')].sort.each { |file| require file } +Dir[File.join(__dir__, 'wordmove/actions/**/*.rb')].each { |file| require file } +Dir[File.join(__dir__, 'wordmove/organizers/**/*.rb')].each { |file| require file } module Wordmove # Interactors' namespce. Interactors are called "Actions", following the LightService convention. diff --git a/lib/wordmove/actions/adapt_local_db.rb b/lib/wordmove/actions/adapt_local_db.rb index 6a7dad06..b03777d6 100644 --- a/lib/wordmove/actions/adapt_local_db.rb +++ b/lib/wordmove/actions/adapt_local_db.rb @@ -9,7 +9,7 @@ module Actions class AdaptLocalDb extend ::LightService::Action include Wordmove::Actions::Helpers - include Wordmove::Wpcli + include Wordmove::WpcliHelpers expects :local_options, :remote_options, @@ -38,63 +38,104 @@ class AdaptLocalDb next context if simulate?(cli_options: context.cli_options) - result = Wordmove::Actions::RunLocalCommand.execute( - cli_options: context.cli_options, - logger: context.logger, - command: mysql_dump_command( - env_db_options: context.local_options[:database], - save_to_path: context.db_paths.local.path - ) - ) - context.fail_and_return!(result.message) if result.failure? + context.logger.task_step true, dump_command(context) + begin + system(dump_command(context), exception: true) + rescue RuntimeError, SystemExit => e + context.fail_and_return!("Local command status reports an error: #{e.message}") + end if context.cli_options[:no_adapt] context.logger.warn 'Skipping DB adapt' else - result = Wordmove::Actions::RunLocalCommand.execute( - cli_options: context.cli_options, - logger: context.logger, - command: wpcli_search_replace_command(context, :vhost) - ) - context.fail_and_return!(result.message) if result.failure? - - result = Wordmove::Actions::RunLocalCommand.execute( - cli_options: context.cli_options, - logger: context.logger, - command: wpcli_search_replace_command(context, :wordpress_path) - ) - context.fail_and_return!(result.message) if result.failure? - end + %i[vhost wordpress_path].each do |key| + command = search_replace_command(context, key) + context.logger.task_step true, command - result = Wordmove::Actions::RunLocalCommand.execute( - cli_options: context.cli_options, + begin + system(command, exception: true) + rescue RuntimeError, SystemExit => e + context.fail_and_return!("Local command status reports an error: #{e.message}") + end + end + end - logger: context.logger, - command: mysql_dump_command( - env_db_options: context.local_options[:database], - save_to_path: context.db_paths.local.adapted_path - ) - ) - context.fail_and_return!(result.message) if result.failure? + context.logger.task_step true, dump_adapted_command(context) + begin + system(dump_adapted_command(context), exception: true) + rescue RuntimeError, SystemExit => e + context.fail_and_return!("Local command status reports an error: #{e.message}") + end if context.photocopier.is_a? Photocopier::SSH - result = Wordmove::Actions::RunLocalCommand.execute( - cli_options: context.cli_options, - logger: context.logger, - command: compress_command(file_path: context.db_paths.local.adapted_path) - ) - context.fail_and_return!(result.message) if result.failure? + context.logger.task_step true, compress_command(context) + begin + system(compress_command(context), exception: true) + rescue RuntimeError, SystemExit => e + context.fail_and_return!("Local command status reports an error: #{e.message}") + end + end + + context.logger.task_step true, import_original_db_command(context) + begin + system(import_original_db_command(context), exception: true) + rescue RuntimeError, SystemExit => e + context.fail_and_return!("Local command status reports an error: #{e.message}") + end + end + + def self.dump_command(context) + "wp db export #{context.db_paths.local.path} --allow-root --quiet " \ + "--path=#{wpcli_config_path(context)}" + end + + def self.dump_adapted_command(context) + "wp db export #{context.db_paths.local.adapted_path} --allow-root --quiet " \ + "--path=#{wpcli_config_path(context)}" + end + + def self.import_original_db_command(context) + "wp db import #{context.db_paths.local.path} --allow-root --quiet " \ + "--path=#{wpcli_config_path(context)}" + end + + def self.compress_command(context) + command = ['nice'] + command << '-n' + command << '0' + command << 'gzip' + command << '-9' + command << '-f' + command << "\"#{context.db_paths.local.adapted_path}\"" + command.join(' ') + end + + # Compose and returns the search-replace command. It's intended to be + # used from a +LightService::Action+ + # + # @param context [LightService::Context] The context of an action + # @param config_key [:vhost, :wordpress_path] Determines what will be replaced in DB + # @return [String] + # @!scope class + def self.search_replace_command(context, config_key) + unless %i[vhost wordpress_path].include?(config_key) + raise ArgumentError, "Unexpected `config_key` #{config_key}.:vhost" \ + 'or :wordpress_path expected' end - result = Wordmove::Actions::RunLocalCommand.execute( - cli_options: context.cli_options, - logger: context.logger, - command: mysql_import_command( - dump_path: context.db_paths.local.path, - env_db_options: context.local_options[:database] - ) - ) - context.fail_and_return!(result.message) if result.failure? + [ + 'wp search-replace', + "--path=#{wpcli_config_path(context)}", + '"\A' + context.dig(:local_options, config_key) + '\Z"', # rubocop:disable Style/StringConcatenation + '"' + context.dig(:remote_options, config_key) + '"', # rubocop:disable Style/StringConcatenation + '--regex-delimiter="|"', + '--regex', + '--precise', + '--quiet', + '--skip-columns=guid', + '--all-tables', + '--allow-root' + ].join(' ') end end end diff --git a/lib/wordmove/actions/adapt_remote_db.rb b/lib/wordmove/actions/adapt_remote_db.rb index 1f4acf8c..9fa59046 100644 --- a/lib/wordmove/actions/adapt_remote_db.rb +++ b/lib/wordmove/actions/adapt_remote_db.rb @@ -14,7 +14,7 @@ module Actions class AdaptRemoteDb extend ::LightService::Action include Wordmove::Actions::Helpers - include Wordmove::Wpcli + include Wordmove::WpcliHelpers expects :local_options, :cli_options, @@ -39,11 +39,12 @@ class AdaptRemoteDb next context if simulate?(cli_options: context.cli_options) if File.exist?(context.db_paths.local.gzipped_path) - Wordmove::Actions::RunLocalCommand.execute( - cli_options: context.cli_options, - logger: context.logger, - command: uncompress_command(file_path: context.db_paths.local.gzipped_path) - ) + context.logger.task_step true, uncompress_command(context) + begin + system(uncompress_command(context), exception: true) + rescue RuntimeError, SystemExit => e + context.fail_and_return!("Local command status reports an error: #{e.message}") + end end unless File.exist?(context.db_paths.local.path) @@ -52,37 +53,76 @@ class AdaptRemoteDb ) end - result = Wordmove::Actions::RunLocalCommand.execute( - cli_options: context.cli_options, - logger: context.logger, - command: mysql_import_command( - dump_path: context.db_paths.local.path, - env_db_options: context.local_options[:database] - ) - ) - context.fail_and_return!(result.message) if result.failure? + context.logger.task_step true, import_db_command(context) + begin + system(import_db_command(context), exception: true) + rescue RuntimeError, SystemExit => e + context.fail_and_return!("Local command status reports an error: #{e.message}") + end if context.cli_options[:no_adapt] context.logger.warn 'Skipping DB adapt' next context end - result = Wordmove::Actions::RunLocalCommand.execute( - cli_options: context.cli_options, - logger: context.logger, - command: wpcli_search_replace_command(context, :vhost) - ) - context.fail_and_return!(result.message) if result.failure? - - result = Wordmove::Actions::RunLocalCommand.execute( - cli_options: context.cli_options, - logger: context.logger, - command: wpcli_search_replace_command(context, :wordpress_path) - ) - context.fail_and_return!(result.message) if result.failure? + %i[vhost wordpress_path].each do |key| + command = search_replace_command(context, key) + context.logger.task_step true, command + begin + system(command, exception: true) + rescue RuntimeError, SystemExit => e + context.fail_and_return!("Local command status reports an error: #{e.message}") + end + end context.logger.success 'Local DB adapted' end + + # Construct the command to deflate a compressed file as a string. + # + # @param file_path [String] The path where the file to be deflated is located + # @return [String] the command + # @!scope class + def self.uncompress_command(context) + command = ['gzip'] + command << '-d' + command << '-f' + command << "\"#{context.db_paths.local.gzipped_path}\"" + command.join(' ') + end + + def self.import_db_command(context) + "wp db import #{context.db_paths.local.path} --allow-root --quiet " \ + "--path=#{wpcli_config_path(context)}" + end + + # Compose and returns the search-replace command. It's intended to be + # used from a +LightService::Action+ + # + # @param context [LightService::Context] The context of an action + # @param config_key [:vhost, :wordpress_path] Determines what will be replaced in DB + # @return [String] + # @!scope class + def self.search_replace_command(context, config_key) + unless %i[vhost wordpress_path].include?(config_key) + raise ArgumentError, "Unexpected `config_key` #{config_key}.:vhost" \ + 'or :wordpress_path expected' + end + + [ + 'wp search-replace', + "--path=#{wpcli_config_path(context)}", + '"\A' + context.dig(:remote_options, config_key) + '\Z"', # rubocop:disable Style/StringConcatenation + '"' + context.dig(:local_options, config_key) + '"', # rubocop:disable Style/StringConcatenation + '--regex-delimiter="|"', + '--regex', + '--precise', + '--quiet', + '--skip-columns=guid', + '--all-tables', + '--allow-root' + ].join(' ') + end end end end diff --git a/lib/wordmove/actions/backup_local_db.rb b/lib/wordmove/actions/backup_local_db.rb index a6a1b695..c6fb152f 100644 --- a/lib/wordmove/actions/backup_local_db.rb +++ b/lib/wordmove/actions/backup_local_db.rb @@ -30,27 +30,41 @@ class BackupLocalDb next context end - result = Wordmove::Actions::RunLocalCommand.execute( - cli_options: context.cli_options, - logger: context.logger, - command: mysql_dump_command( - env_db_options: context.local_options[:database], - save_to_path: context.db_paths.backup.local.path - ) - ) - context.fail_and_return!(result.message) if result.failure? + context.logger.task_step true, dump_command(context) - result = Wordmove::Actions::RunLocalCommand.execute( - cli_options: context.cli_options, - logger: context.logger, - command: compress_command(file_path: context.db_paths.backup.local.path) - ) - context.fail_and_return!(result.message) if result.failure? + begin + system(dump_command(context), exception: true) + rescue RuntimeError, SystemExit => e + context.fail_and_return!("Local command status reports an error: #{e.message}") + end + + context.logger.task_step true, compress_command(context) + + begin + system(compress_command(context), exception: true) + rescue RuntimeError, SystemExit => e + context.fail_and_return!("Local command status reports an error: #{e.message}") + end context.logger.success( "Backup saved at #{context.db_paths.backup.local.gzipped_path}" ) end + + def self.dump_command(context) + "wp db export #{context.db_paths.backup.local.path} --allow-root --quiet" + end + + def self.compress_command(context) + command = ['nice'] + command << '-n' + command << '0' + command << 'gzip' + command << '-9' + command << '-f' + command << "\"#{context.db_paths.backup.local.path}\"" + command.join(' ') + end end end end diff --git a/lib/wordmove/assets/wordmove_schema_local.yml b/lib/wordmove/assets/wordmove_schema_local.yml index e2cbe6c5..c8e0d55a 100644 --- a/lib/wordmove/assets/wordmove_schema_local.yml +++ b/lib/wordmove/assets/wordmove_schema_local.yml @@ -3,6 +3,7 @@ mapping: vhost: pattern: /^https?:\/\// wordpress_path: + required: true database: type: map required: true @@ -15,9 +16,6 @@ mapping: required: true host: required: true - mysqldump_options: - port: - charset: paths: type: map mapping: diff --git a/lib/wordmove/cli.rb b/lib/wordmove/cli.rb index f127227e..ff9c42d4 100644 --- a/lib/wordmove/cli.rb +++ b/lib/wordmove/cli.rb @@ -47,8 +47,8 @@ def movefile_from(**cli_options) exit 1 end - def call_organizer_with(klass:, movefile:, **cli_options) - result = klass.call(cli_options: cli_options, movefile: movefile) + def call_organizer_with(klass:, movefile:, cli_options:) + result = klass.call(cli_options, movefile) exit 0 if result.success? @@ -114,19 +114,19 @@ def call(**cli_options) private def call_pull_organizer_with(**cli_options) - movefile = movefile_from(**cli_options) + movefile = movefile_from(cli_options) if movefile.options.dig(movefile.environment, :ssh) call_organizer_with( klass: Wordmove::Organizers::Ssh::Pull, movefile: movefile, - **cli_options + cli_options: cli_options ) elsif movefile.options.dig(movefile.environment, :ftp) call_organizer_with( klass: Wordmove::Organizers::Ftp::Pull, movefile: movefile, - **cli_options + cli_options: cli_options ) else raise NoAdapterFound, 'No valid adapter found.' @@ -149,19 +149,19 @@ def call(**cli_options) private def call_push_organizer_with(**cli_options) - movefile = movefile_from(**cli_options) + movefile = movefile_from(cli_options) if movefile.options.dig(movefile.environment, :ssh) call_organizer_with( klass: Wordmove::Organizers::Ssh::Push, movefile: movefile, - **cli_options + cli_options: cli_options ) elsif movefile.options.dig(movefile.environment, :ftp) call_organizer_with( klass: Wordmove::Organizers::Ftp::Push, movefile: movefile, - **cli_options + cli_options: cli_options ) else raise NoAdapterFound, 'No valid adapter found.' diff --git a/lib/wordmove/generators/movefile.rb b/lib/wordmove/generators/movefile.rb index 525b2447..826284f4 100644 --- a/lib/wordmove/generators/movefile.rb +++ b/lib/wordmove/generators/movefile.rb @@ -1,16 +1,15 @@ module Wordmove module Generators class Movefile - include MovefileAdapter - def self.generate - new.copy_movefile + copy_movefile end - def copy_movefile + def self.copy_movefile + wordpress_path = File.expand_path(Dir.pwd) content = ERB.new(File.read(File.join(__dir__, 'movefile.yml'))).result(binding) - files = Dry::Files.new + files = Dry::Files.new files.write('movefile.yml', content) end end diff --git a/lib/wordmove/generators/movefile.yml b/lib/wordmove/generators/movefile.yml index 8aadcb74..3207d6fe 100644 --- a/lib/wordmove/generators/movefile.yml +++ b/lib/wordmove/generators/movefile.yml @@ -2,14 +2,15 @@ global: sql_adapter: wpcli local: - vhost: http://vhost.local wordpress_path: <%= wordpress_path %> # use an absolute path here - database: - name: <%= database.name %> - user: <%= database.user %> - password: "<%= database.password %>" # could be blank, so always use quotes around - host: <%= database.host %> + # paths: # you can customize wordpress internal paths + # wp_content: wp-content + # uploads: wp-content/uploads + # plugins: wp-content/plugins + # mu_plugins: wp-content/mu-plugins + # themes: wp-content/themes + # languages: wp-content/languages production: vhost: http://example.com @@ -40,6 +41,7 @@ production: - 'wp-config.php' - 'wp-content/*.sql.gz' - '*.orig' + - 'wp-cli.yml' # paths: # you can customize wordpress internal paths # wp_content: wp-content diff --git a/lib/wordmove/generators/movefile_adapter.rb b/lib/wordmove/generators/movefile_adapter.rb deleted file mode 100644 index 070ff18d..00000000 --- a/lib/wordmove/generators/movefile_adapter.rb +++ /dev/null @@ -1,89 +0,0 @@ -module Wordmove - module Generators - module MovefileAdapter - def wordpress_path - File.expand_path(Dir.pwd) - end - - def database - DBConfigReader.config - end - end - - class DBConfigReader - def self.config - new.config - end - - def config - Struct.new( - :name, - :user, - :password, - :host, - keyword_init: true - ).new(database_config) - end - - def database_config - if wp_config_exists? - WordpressDBConfig.config - else - DefaultDBConfig.config - end - end - - def wp_config_exists? - File.exist?(WordpressDirectory.default_path_for(:wp_config)) - end - end - - class DefaultDBConfig - def self.config - { - name: 'database_name', - user: 'user', - password: 'password', - host: '127.0.0.1' - } - end - end - - class WordpressDBConfig - def self.config - new.config - end - - def wp_config - @wp_config ||= File.read( - WordpressDirectory.default_path_for(:wp_config) - ).encode('utf-8', invalid: :replace) - end - - def wp_definitions - { - name: 'DB_NAME', - user: 'DB_USER', - password: 'DB_PASSWORD', - host: 'DB_HOST' - } - end - - def wp_definition_regex(definition) - /['"]#{definition}['"],\s*["'](?.*)['"]/ - end - - def defaults - DefaultDBConfig.config.clone - end - - def config - wp_definitions.each_with_object(defaults) do |(key, definition), result| - wp_config.match(wp_definition_regex(definition)) do |match| - result[key] = match[:value] - end - end - end - end - end -end diff --git a/lib/wordmove/movefile.rb b/lib/wordmove/movefile.rb index 726d2608..8d20d761 100644 --- a/lib/wordmove/movefile.rb +++ b/lib/wordmove/movefile.rb @@ -56,8 +56,6 @@ def secrets private def fetch(verbose = true) # rubocop:disable Style/OptionalBooleanParameter - load_dotenv - entries = if config_file_name.nil? Dir["#{File.join(start_dir, '{M,m}ovefile')}{,.yml,.yaml}"] else @@ -75,18 +73,40 @@ def fetch(verbose = true) # rubocop:disable Style/OptionalBooleanParameter end found = entries.first + logger.task("Using Movefile: #{found}") if verbose == true - YAML.safe_load(ERB.new(File.read(found)).result, [], [], true).deep_symbolize_keys! + load_dotenv(verbose) + + options = YAML.safe_load(ERB.new(File.read(found)).result, [], [], true).deep_symbolize_keys! + + merge_local_options_from_wpcli(options) + end + + def merge_local_options_from_wpcli(options) + config_path = options.dig(:local, :wordpress_path) + + options.merge( + local: { + database: { + password: Wordmove::WpcliHelpers.get_config('DB_PASSWORD', config_path: config_path), + host: Wordmove::WpcliHelpers.get_config('DB_HOST', config_path: config_path), + name: Wordmove::WpcliHelpers.get_config('DB_NAME', config_path: config_path), + user: Wordmove::WpcliHelpers.get_config('DB_USER', config_path: config_path) + }, + vhost: Wordmove::WpcliHelpers.get_option('siteurl', config_path: config_path), + wordpress_path: config_path + } + ) end - def load_dotenv + def load_dotenv(verbose) env_files = Dir[File.join(start_dir, '.env')] found_env = env_files.first return false unless found_env.present? - logger.info("Using .env file: #{found_env}") + logger.info("Using .env file: #{found_env}") if verbose Dotenv.load(found_env) end diff --git a/lib/wordmove/organizers/ftp/pull.rb b/lib/wordmove/organizers/ftp/pull.rb index 4e3845d7..e95a1756 100644 --- a/lib/wordmove/organizers/ftp/pull.rb +++ b/lib/wordmove/organizers/ftp/pull.rb @@ -6,7 +6,9 @@ class Pull include Wordmove::Actions::Helpers include Wordmove::Actions::Ftp::Helpers - def self.call(cli_options:, movefile:) + # Can't use keyword arguments since LightService still has some problems with modern + # ruby syntax: https://github.com/adomokos/light-service/pull/224 + def self.call(cli_options, movefile) logger = Logger.new($stdout, movefile.secrets).tap { |l| l.level = Logger::DEBUG } remote_options = movefile.options[movefile.environment] ftp_opts = ftp_options(remote_options: remote_options) diff --git a/lib/wordmove/organizers/ftp/push.rb b/lib/wordmove/organizers/ftp/push.rb index 61bcc47f..5e1519a0 100644 --- a/lib/wordmove/organizers/ftp/push.rb +++ b/lib/wordmove/organizers/ftp/push.rb @@ -6,7 +6,9 @@ class Push include Wordmove::Actions::Helpers include Wordmove::Actions::Ftp::Helpers - def self.call(cli_options:, movefile:) + # Can't use keyword arguments since LightService still has some problems with modern + # ruby syntax: https://github.com/adomokos/light-service/pull/224 + def self.call(cli_options, movefile) logger = Logger.new($stdout, movefile.secrets).tap { |l| l.level = Logger::DEBUG } remote_options = movefile.options[movefile.environment] ftp_opts = ftp_options(remote_options: remote_options) diff --git a/lib/wordmove/organizers/ssh/pull.rb b/lib/wordmove/organizers/ssh/pull.rb index b5c2402d..7bf2331f 100644 --- a/lib/wordmove/organizers/ssh/pull.rb +++ b/lib/wordmove/organizers/ssh/pull.rb @@ -6,7 +6,9 @@ class Pull include Wordmove::Actions::Helpers include Wordmove::Actions::Ssh::Helpers - def self.call(cli_options:, movefile:) + # Can't use keyword arguments since LightService still has some problems with modern + # ruby syntax: https://github.com/adomokos/light-service/pull/224 + def self.call(cli_options, movefile) logger = Logger.new($stdout, movefile.secrets).tap { |l| l.level = Logger::DEBUG } remote_options = movefile.options[movefile.environment] ssh_opts = ssh_options(remote_options: remote_options, simulate: cli_options[:simulate]) diff --git a/lib/wordmove/organizers/ssh/push.rb b/lib/wordmove/organizers/ssh/push.rb index e1e03e8d..e3548fd1 100644 --- a/lib/wordmove/organizers/ssh/push.rb +++ b/lib/wordmove/organizers/ssh/push.rb @@ -6,7 +6,9 @@ class Push include Wordmove::Actions::Helpers include Wordmove::Actions::Ssh::Helpers - def self.call(cli_options:, movefile:) + # Can't use keyword arguments since LightService still has some problems with modern + # ruby syntax: https://github.com/adomokos/light-service/pull/224 + def self.call(cli_options, movefile) logger = Logger.new($stdout, movefile.secrets).tap { |l| l.level = Logger::DEBUG } remote_options = movefile.options[movefile.environment] ssh_opts = ssh_options(remote_options: remote_options, simulate: cli_options[:simulate]) diff --git a/lib/wordmove/wpcli.rb b/lib/wordmove/wpcli.rb index 878ddbc0..fc316b45 100644 --- a/lib/wordmove/wpcli.rb +++ b/lib/wordmove/wpcli.rb @@ -1,7 +1,7 @@ module Wordmove # This class is a sort of mini-wrapper around the wp-cli executable. # It's responsible to run or produce wp-cli commands. - module Wpcli + module WpcliHelpers extend ActiveSupport::Concern included do @@ -17,42 +17,27 @@ def wp_in_path? system('which wp > /dev/null 2>&1') end - # Compose and returns the search-replace command. It's intended to be - # used from a +LightService::Action+ - # - # @param context [LightService::Context] The context of an action - # @param config_key [:vhost, :wordpress_path] Determines what will be replaced in DB - # @return [String] - # @!scope class - def wpcli_search_replace_command(context, config_key) - unless %i[vhost wordpress_path].include?(config_key) - raise ArgumentError, "Unexpected `config_key` #{config_key}.:vhost" \ - 'or :wordpress_path expected' - end - - [ - 'wp search-replace', - "--path=#{wpcli_config_path(context)}", - '"\A' + context.dig(:remote_options, config_key) + '\Z"', # rubocop:disable Style/StringConcatenation - '"' + context.dig(:local_options, config_key) + '"', # rubocop:disable Style/StringConcatenation - '--regex-delimiter="|"', - '--regex', - '--precise', - '--quiet', - '--skip-columns=guid', - '--all-tables', - '--allow-root' - ].join(' ') - end - # Returns the wordpress path from wp-cli (with precedence) or from movefile # - # It's intended to be used from a +LightService::Action+ + # It's intended to be used from a +LightService::Action+, but it also supports + # to receive a path as argument. If the argument is not a LightService::Context + # then it will be treated as a path. + # The path passed as argument should be the wordpress installation path, but it's + # not strictly mandatory: the method will try to load a wpcli's YAML config + # from that path, so you can potentially use it with any path # - # @param context [LightService::Context] The context of an action + # @param context [LightService::Context|String] The context of an action or a path as string # @return [String] # @!scope class - def wpcli_config_path(context) + def wpcli_config_path(context_or_path) + context = if context_or_path.is_a? LightService::Context + context_or_path + else + # We need to make it quack like a duck in order to be + # backward compatible with previous code + { local_options: { wordpress_path: context_or_path } } + end + load_from_yml(context) || load_from_wpcli || context.dig(:local_options, :wordpress_path) end @@ -63,7 +48,8 @@ def wpcli_config_path(context) # @!scope class # @!visibility private def load_from_yml(context) - yml_path = File.join(context.dig(:local_options, :wordpress_path), 'wp-cli.yml') + config_path = context.dig(:local_options, :wordpress_path) || '.' + yml_path = File.join(config_path, 'wp-cli.yml') return unless File.exist?(yml_path) @@ -87,5 +73,13 @@ def load_from_wpcli nil end end + + def self.get_option(option, config_path:) + `wp option get #{option} --allow-root --path=#{config_path}`.chomp + end + + def self.get_config(config, config_path:) + `wp config get #{config} --allow-root --path=#{config_path}`.chomp + end end end diff --git a/spec/actions/adapt_local_db_spec.rb b/spec/actions/adapt_local_db_spec.rb index 3a0dbe13..b3445354 100644 --- a/spec/actions/adapt_local_db_spec.rb +++ b/spec/actions/adapt_local_db_spec.rb @@ -14,8 +14,6 @@ ] end - let(:local_command_stub) { class_double('Wordmove::Actions::RunLocalCommand').as_stubbed_const } - before do silence_logger! # Note we're stubbing subsequent actions from organizer. @@ -23,7 +21,10 @@ stubbed_actions.each do |action| stub_action(action) end - stub_action(local_command_stub) + + allow(described_class) + .to receive(:system) + .and_return(true) end it 'works like it should' do @@ -31,10 +32,7 @@ context ) - aggregate_failures 'testing sub-actions' do - expect(local_command_stub).to have_received(:execute).exactly(6).times - expect(result).to be_success - end + expect(result).to be_success end context 'when --no-adapt' do @@ -50,8 +48,26 @@ ) aggregate_failures 'testing sub-actions' do - expect(local_command_stub).to have_received(:execute).exactly(4).times expect(result).to be_success + expect(described_class) + .to_not have_received(:system) + .with(/wp search-replace/, exception: true) + end + end + end + + context '.search_replace_command' do + it 'returns the expected command' do + expect(subject.class.search_replace_command(context, :wordpress_path)) + .to eq('wp search-replace --path=~/dev/sites/your_site "\A~/dev/sites/your_site\Z" ' \ + '"/var/www/your_site" --regex-delimiter="|" --regex --precise --quiet ' \ + '--skip-columns=guid --all-tables --allow-root') + end + + context 'when wrong config_key is passed' do + it 'raises an error' do + expect { subject.class.search_replace_command(context, :wrong) } + .to raise_error(ArgumentError) end end end diff --git a/spec/actions/backup_local_db_spec.rb b/spec/actions/backup_local_db_spec.rb new file mode 100644 index 00000000..7c02548a --- /dev/null +++ b/spec/actions/backup_local_db_spec.rb @@ -0,0 +1,77 @@ +require 'spec_helper' + +# I know these tests are very weak. I don't know how to make them sturdier +# and having them is better than nothing :) +describe Wordmove::Actions::BackupLocalDb do + let(:context) do + OrganizerContextFactory.make_for(described_class, :pull, cli_options: { db: true }) + end + + let(:stubbed_actions) do + [ + Wordmove::Actions::Ssh::DownloadRemoteDb + ] + end + + before do + silence_logger! + # Note we're stubbing subsequent actions from organizer. + # This stubs could be useful for using spies on classes. + stubbed_actions.each do |action| + stub_action(action) + end + end + + it 'works like it should' do + allow(described_class).to receive(:system).and_return(true) + + result = described_class.execute( + context + ) + + expect(result).to be_success + end + + context 'when system dump command fails' do + before do + allow(described_class) + .to receive(:system) + .with(/wp db export/, exception: true) + .and_raise(RuntimeError.new('Foo')) + end + + it 'fails and reports the error' do + result = described_class.execute( + context + ) + + aggregate_failures do + expect(result).to be_failure + expect(result.message).to match('Foo') + end + end + end + + context 'when system compress command fails' do + before do + allow(described_class) + .to receive(:system) + .with(/wp db export/, exception: true) + + allow(described_class) + .to receive(:system) + .with(/gzip/, exception: true) + .and_raise(RuntimeError.new('Bar')) + end + it 'fails and reports the error' do + result = described_class.execute( + context + ) + + aggregate_failures do + expect(result).to be_failure + expect(result.message).to match('Bar') + end + end + end +end diff --git a/spec/docotor_spec.rb b/spec/doctor_spec.rb similarity index 100% rename from spec/docotor_spec.rb rename to spec/doctor_spec.rb diff --git a/spec/features/movefile_spec.rb b/spec/features/movefile_spec.rb index d18ada3c..78cddb05 100644 --- a/spec/features/movefile_spec.rb +++ b/spec/features/movefile_spec.rb @@ -27,14 +27,6 @@ expect(yaml['local']['wordpress_path']).to eq(Dir.pwd) end - it 'fills database configuration defaults' do - yaml = YAML.safe_load(ERB.new(File.read(movefile)).result) - expect(yaml['local']['database']['name']).to eq('database_name') - expect(yaml['local']['database']['user']).to eq('user') - expect(yaml['local']['database']['password']).to eq('password') - expect(yaml['local']['database']['host']).to eq('127.0.0.1') - end - it 'creates a Movifile having a "global.sql_adapter" key' do yaml = YAML.safe_load(ERB.new(File.read(movefile)).result) expect(yaml['global']).to be_present @@ -42,21 +34,4 @@ expect(yaml['global']['sql_adapter']).to eq('wpcli') end end - - context 'database configuration' do - let(:wp_config) { File.join(File.dirname(__FILE__), '../fixtures/wp-config.php') } - - before do - FileUtils.cp(wp_config, '.') - silence_stream($stdout) { Wordmove::Generators::Movefile.generate } - end - - it 'fills database configuration from wp-config' do - yaml = YAML.safe_load(ERB.new(File.read(movefile)).result) - expect(yaml['local']['database']['name']).to eq('wordmove_db') - expect(yaml['local']['database']['user']).to eq('wordmove_user') - expect(yaml['local']['database']['password']).to eq('wordmove_password') - expect(yaml['local']['database']['host']).to eq('wordmove_host') - end - end end diff --git a/spec/fixtures/movefiles/Movefile b/spec/fixtures/movefiles/Movefile index 12dd4d68..d24f1592 100644 --- a/spec/fixtures/movefiles/Movefile +++ b/spec/fixtures/movefiles/Movefile @@ -1,13 +1,8 @@ global: - sql_adapter: "default" + sql_adapter: "wpcli" local: vhost: "http://vhost.local" wordpress_path: "~/dev/sites/your_site" - database: - name: "database_name" - user: "user" - password: "password" - host: "host" remote: vhost: "http://example.com" wordpress_path: "/var/www/your_site" diff --git a/spec/fixtures/movefiles/custom_paths b/spec/fixtures/movefiles/custom_paths index 5886f5ec..8fef9cb6 100644 --- a/spec/fixtures/movefiles/custom_paths +++ b/spec/fixtures/movefiles/custom_paths @@ -1,13 +1,7 @@ global: - sql_adapter: "default" + sql_adapter: "wpcli" local: - vhost: "http://vhost.local" wordpress_path: "~/dev/sites/your_site" - database: - name: "database_name" - user: "user" - password: "password" - host: "host" paths: uploads: 'wp-content/pirate' remote: diff --git a/spec/fixtures/movefiles/multi_environments b/spec/fixtures/movefiles/multi_environments index 343b217b..db8daa01 100644 --- a/spec/fixtures/movefiles/multi_environments +++ b/spec/fixtures/movefiles/multi_environments @@ -2,15 +2,8 @@ global: sql_adapter: "wpcli" local: - vhost: "http://localhost:8080" wordpress_path: "/home/welaika/sites/your_site" - database: - name: "database_name" - user: "user" - password: "password" - host: "host" - staging: vhost: "http://staging.mysite.example.com" wordpress_path: "/var/www/your_site" # use an absolute path here diff --git a/spec/fixtures/movefiles/multi_environments_wpcli_sql_adapter b/spec/fixtures/movefiles/multi_environments_wpcli_sql_adapter index 343b217b..db8daa01 100644 --- a/spec/fixtures/movefiles/multi_environments_wpcli_sql_adapter +++ b/spec/fixtures/movefiles/multi_environments_wpcli_sql_adapter @@ -2,15 +2,8 @@ global: sql_adapter: "wpcli" local: - vhost: "http://localhost:8080" wordpress_path: "/home/welaika/sites/your_site" - database: - name: "database_name" - user: "user" - password: "password" - host: "host" - staging: vhost: "http://staging.mysite.example.com" wordpress_path: "/var/www/your_site" # use an absolute path here diff --git a/spec/fixtures/movefiles/with_forbidden_tasks b/spec/fixtures/movefiles/with_forbidden_tasks index 3a0eecb4..2e0399fd 100644 --- a/spec/fixtures/movefiles/with_forbidden_tasks +++ b/spec/fixtures/movefiles/with_forbidden_tasks @@ -1,13 +1,7 @@ global: - sql_adapter: "default" + sql_adapter: "wpcli" local: - vhost: "http://vhost.local" wordpress_path: "~/dev/sites/your_site" - database: - name: "database_name" - user: "user" - password: "password" - host: "host" remote: vhost: "http://example.com" wordpress_path: "/var/www/your_site" diff --git a/spec/fixtures/movefiles/with_hooks b/spec/fixtures/movefiles/with_hooks index a7478bd3..b9569332 100644 --- a/spec/fixtures/movefiles/with_hooks +++ b/spec/fixtures/movefiles/with_hooks @@ -1,18 +1,11 @@ <% require 'tmpdir' %> global: - sql_adapter: "default" + sql_adapter: "wpcli" local: - vhost: "http://localhost:8080" wordpress_path: "<%= Dir.tmpdir %>" - database: - name: "database_name" - user: "user" - password: "password" - host: "host" - ssh_with_hooks: vhost: "http://staging.mysite.example.com" wordpress_path: "/var/www/your_site" # use an absolute path here diff --git a/spec/fixtures/movefiles/with_secrets b/spec/fixtures/movefiles/with_secrets index c6b8aba0..aa72f987 100644 --- a/spec/fixtures/movefiles/with_secrets +++ b/spec/fixtures/movefiles/with_secrets @@ -1,13 +1,7 @@ global: - sql_adapter: "default" + sql_adapter: "wpcli" local: - vhost: "http://secrets.local" wordpress_path: "~/dev/sites/your_site" - database: - name: "database_name" - user: "user" - password: "local_database_password" - host: "local_database_host" remote: vhost: "http://secrets.example.com" wordpress_path: "/var/www/your_site" diff --git a/spec/fixtures/movefiles/with_secrets_with_empty_local_db_password b/spec/fixtures/movefiles/with_secrets_with_empty_local_db_password index 61ef7467..0236e8f0 100644 --- a/spec/fixtures/movefiles/with_secrets_with_empty_local_db_password +++ b/spec/fixtures/movefiles/with_secrets_with_empty_local_db_password @@ -1,13 +1,7 @@ global: - sql_adapter: "default" + sql_adapter: "wpcli" local: - vhost: "http://secrets.local" wordpress_path: "~/dev/sites/your_site" - database: - name: "database_name" - user: "user" - password: "" - host: "local_database_host" remote: vhost: "http://secrets.example.com" wordpress_path: "/var/www/your_site" diff --git a/spec/hook_spec.rb b/spec/hook_spec.rb index 1903819c..426ff2ff 100644 --- a/spec/hook_spec.rb +++ b/spec/hook_spec.rb @@ -54,7 +54,7 @@ end it 'checks the order' do - Wordmove::Organizers::Ssh::Push.call(context) + Wordmove::Organizers::Ssh::Push.call(context[:cli_options], context[:movefile]) expect(Wordmove::Hook::Local).to( have_received(:run).with( @@ -107,31 +107,31 @@ let(:options) { common_options.merge(environment: 'ssh_with_hooks') } it 'runs registered before local hooks' do - expect { Wordmove::Organizers::Ssh::Push.call(context) } + expect { Wordmove::Organizers::Ssh::Push.call(context[:cli_options], context[:movefile]) } .to output(/Calling hook push before local/) .to_stdout end it 'runs registered before local hooks in the wordpress folder' do - expect { Wordmove::Organizers::Ssh::Push.call(context) } + expect { Wordmove::Organizers::Ssh::Push.call(context[:cli_options], context[:movefile]) } .to output(/#{Dir.tmpdir}/) .to_stdout end it 'runs registered before remote hooks' do - expect { Wordmove::Organizers::Ssh::Push.call(context) } + expect { Wordmove::Organizers::Ssh::Push.call(context[:cli_options], context[:movefile]) } .to output(/Calling hook push before remote/) .to_stdout end it 'runs registered after local hooks' do - expect { Wordmove::Organizers::Ssh::Push.call(context) } + expect { Wordmove::Organizers::Ssh::Push.call(context[:cli_options], context[:movefile]) } .to output(/Calling hook push after local/) .to_stdout end it 'runs registered after remote hooks' do - expect { Wordmove::Organizers::Ssh::Push.call(context) } + expect { Wordmove::Organizers::Ssh::Push.call(context[:cli_options], context[:movefile]) } .to output(/Calling hook push after remote/) .to_stdout end @@ -142,7 +142,7 @@ end it 'does not really run any commands' do - expect { Wordmove::Organizers::Ssh::Push.call(context) } + expect { Wordmove::Organizers::Ssh::Push.call(context[:cli_options], context[:movefile]) } .not_to output(/Output:/) .to_stdout end @@ -154,7 +154,7 @@ it 'logs an error and raises a LocalHookException' do expect do expect do - Wordmove::Organizers::Ssh::Push.call(context) + Wordmove::Organizers::Ssh::Push.call(context[:cli_options], context[:movefile]) end.to raise_exception(Wordmove::LocalHookException) end.to output(/Error code: 127/).to_stdout end @@ -167,7 +167,7 @@ it 'logs an error without raising an exeption' do expect do expect do - Wordmove::Organizers::Ssh::Push.call(context) + Wordmove::Organizers::Ssh::Push.call(context[:cli_options], context[:movefile]) end.to_not raise_exception end.to output(/Error code: 127/) .to_stdout @@ -187,31 +187,31 @@ let(:options) { common_options.merge(environment: 'ssh_with_hooks') } it 'runs registered before local hooks' do - expect { Wordmove::Organizers::Ssh::Pull.call(context) } + expect { Wordmove::Organizers::Ssh::Pull.call(context[:cli_options], context[:movefile]) } .to output(/Calling hook pull before local/) .to_stdout end it 'runs registered before remote hooks' do - expect { Wordmove::Organizers::Ssh::Pull.call(context) } + expect { Wordmove::Organizers::Ssh::Pull.call(context[:cli_options], context[:movefile]) } .to output(/Calling hook pull before remote/) .to_stdout end it 'runs registered after local hooks' do - expect { Wordmove::Organizers::Ssh::Pull.call(context) } + expect { Wordmove::Organizers::Ssh::Pull.call(context[:cli_options], context[:movefile]) } .to output(/Calling hook pull after local/) .to_stdout end it 'runs registered after remote hooks' do - expect { Wordmove::Organizers::Ssh::Pull.call(context) } + expect { Wordmove::Organizers::Ssh::Pull.call(context[:cli_options], context[:movefile]) } .to output(/Calling hook pull after remote/) .to_stdout end it 'return remote stdout' do - expect { Wordmove::Organizers::Ssh::Pull.call(context) } + expect { Wordmove::Organizers::Ssh::Pull.call(context[:cli_options], context[:movefile]) } .to output(/Stubbed remote stdout/) .to_stdout end @@ -227,7 +227,7 @@ it 'returns remote stdout and raise an exception' do expect do expect do - Wordmove::Organizers::Ssh::Pull.call(context) + Wordmove::Organizers::Ssh::Pull.call(context[:cli_options], context[:movefile]) end.to raise_exception(Wordmove::RemoteHookException) end.to output(/Stubbed remote stderr/) .to_stdout @@ -236,7 +236,7 @@ it 'raises a RemoteHookException' do expect do silence_stream($stdout) do - Wordmove::Organizers::Ssh::Pull.call(context) + Wordmove::Organizers::Ssh::Pull.call(context[:cli_options], context[:movefile]) end end.to raise_exception(Wordmove::RemoteHookException) end @@ -251,7 +251,7 @@ expect(Wordmove::Hook::Remote) .to_not receive(:run) - expect { Wordmove::Organizers::Ftp::Push.call(**context) } + expect { Wordmove::Organizers::Ftp::Push.call(context[:cli_options], context[:movefile]) } .to output( /You have configured remote hooks to run over an FTP connection, but this is not possible/ # rubocop:disable Layout/LineLength ).to_stdout @@ -269,12 +269,12 @@ .to_not receive(:run) silence_stream($stdout) do - Wordmove::Organizers::Ssh::Push.call(context) + Wordmove::Organizers::Ssh::Push.call(context[:cli_options], context[:movefile]) end end it "works silently ignoring 'before' step is not present" do - expect { Wordmove::Organizers::Ssh::Pull.call(context) } + expect { Wordmove::Organizers::Ssh::Pull.call(context[:cli_options], context[:movefile]) } .to output(/I've partially configured my hooks/) .to_stdout end diff --git a/spec/movefile_spec.rb b/spec/movefile_spec.rb index c4f05ddd..264a72a4 100644 --- a/spec/movefile_spec.rb +++ b/spec/movefile_spec.rb @@ -159,7 +159,7 @@ %w[ local_database_password local_database_host - http://secrets.local + http://example.com ~/dev/sites/your_site remote_database_password remote_database_host @@ -175,12 +175,17 @@ end it 'returns all the secrets found in movefile excluding empty string values' do + allow(Wordmove::WpcliHelpers) + .to receive(:get_config) + .with('DB_PASSWORD', config_path: instance_of(String)) + .and_return('') + path = movefile_path_for('with_secrets_with_empty_local_db_password') movefile = described_class.new(config: path) expect(movefile.secrets).to eq( %w[ local_database_host - http://secrets.local + http://example.com ~/dev/sites/your_site remote_database_password remote_database_host diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index fa2811b0..e3e017c6 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -10,7 +10,7 @@ require 'wordmove' -Dir[File.expand_path('support/**/*.rb', __dir__)].sort.each { |f| require f } +Dir[File.expand_path('support/**/*.rb', __dir__)].each { |f| require f } # I don't know from where this method was imported, # but since last updates it was lost. I looked about @@ -28,7 +28,7 @@ def silence_stream(stream) old_stream.close end -RSpec.configure do |config| +RSpec.configure do |config| # rubocop:disable Metrics/BlockLength config.expect_with :rspec do |expectations| expectations.include_chain_clauses_in_custom_matcher_descriptions = true end @@ -41,4 +41,34 @@ def silence_stream(stream) config.example_status_persistence_file_path = './spec/examples.txt' config.formatter = :documentation + + config.before :each do + allow(Wordmove::WpcliHelpers) + .to receive(:get_option) + .and_return('an option') + + allow(Wordmove::WpcliHelpers) + .to receive(:get_option) + .with('siteurl', config_path: instance_of(String)) + .and_return('http://example.com') + + allow(Wordmove::WpcliHelpers) + .to receive(:get_config) + .and_return('a config') + + allow(Wordmove::WpcliHelpers) + .to receive(:get_config) + .with('DB_PASSWORD', config_path: instance_of(String)) + .and_return('local_database_password') + + allow(Wordmove::WpcliHelpers) + .to receive(:get_config) + .with('DB_HOST', config_path: instance_of(String)) + .and_return('local_database_host') + + allow(Wordmove::WpcliHelpers) + .to receive(:get_config) + .with('DB_NAME', config_path: instance_of(String)) + .and_return('local_database_name') + end end diff --git a/spec/support/action_helpers.rb b/spec/support/action_helpers.rb index 5d56c7b7..543472ca 100644 --- a/spec/support/action_helpers.rb +++ b/spec/support/action_helpers.rb @@ -45,10 +45,7 @@ def self.make_for(action, wordmove_action, cli_options: {}) LightService::Testing::ContextFactory .make_from("Wordmove::Organizers::Ssh::#{wordmove_action.to_s.camelize}".constantize) .for(action) - .with( - cli_options: cli_options, - movefile: movefile - ) + .with(cli_options, movefile) end end diff --git a/spec/wordpress_directory_spec.rb b/spec/wordpress_directory_spec.rb index 9e50bc96..bfb4b6af 100644 --- a/spec/wordpress_directory_spec.rb +++ b/spec/wordpress_directory_spec.rb @@ -50,14 +50,14 @@ context 'given an additional path as a string' do it 'returns the URL of the folder joined with the additional path' do wd = described_class.new(:uploads, options) - expect(wd.url('pirate.png')).to eq('http://vhost.local/wp-content/uploads/pirate.png') + expect(wd.url('pirate.png')).to eq('http://example.com/wp-content/uploads/pirate.png') end end context 'without arguments' do it 'returns the URL for the required folder' do wd = described_class.new(:uploads, options) - expect(wd.url).to eq('http://vhost.local/wp-content/uploads') + expect(wd.url).to eq('http://example.com/wp-content/uploads') end end end diff --git a/spec/wpcli_spec.rb b/spec/wpcli_spec.rb index 26ef11dd..200bd74b 100644 --- a/spec/wpcli_spec.rb +++ b/spec/wpcli_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe Wordmove::Wpcli do +describe Wordmove::WpcliHelpers do subject do Class.new do - include Wordmove::Wpcli + include Wordmove::WpcliHelpers end end @@ -20,6 +20,12 @@ end end + context 'when called with a path instead of a config' do + it 'not finding any path using wpcli it will return the path passed as argument' do + expect(subject.wpcli_config_path('/path/to/biscuit')).to eq('/path/to/biscuit') + end + end + context 'when there is not wp-cli.yml in wordpress root directory' do context 'if wp-cli is configured someway with a custom path' do before do @@ -32,6 +38,12 @@ it 'returns the configured path' do expect(subject.wpcli_config_path(a_context)).to eq('/path/to/pudding') end + + context 'when called with a path instead of a config' do + it 'returns the configured path anyway' do + expect(subject.wpcli_config_path('/path/to/biscuit')).to eq('/path/to/pudding') + end + end end context 'when wp-cli param-dump returns empty string' do @@ -49,21 +61,4 @@ end end end - - context '.wpcli_search_replace_command' do - it 'returns the expected command' do - a_context[:local_options][:wordpress_path] = fixture_folder_root_relative_path - expect(subject.wpcli_search_replace_command(a_context, :wordpress_path)) - .to eq('wp search-replace --path=/path/to/steak "\A/var/www/your_site\Z" ' \ - '"spec/fixtures" --regex-delimiter="|" --regex --precise --quiet ' \ - '--skip-columns=guid --all-tables --allow-root') - end - - context 'when wrong config_key is passed' do - it 'raises an error' do - expect { subject.wpcli_search_replace_command(a_context, :wrong) } - .to raise_error(ArgumentError) - end - end - end end diff --git a/wordmove.gemspec b/wordmove.gemspec index 2b5b272f..ef7b8b30 100644 --- a/wordmove.gemspec +++ b/wordmove.gemspec @@ -41,7 +41,7 @@ Gem::Specification.new do |spec| spec.add_runtime_dependency 'dry-cli', '~> 0.7.0' spec.add_runtime_dependency 'dry-files', '~> 0.1.0' - spec.required_ruby_version = '>= 2.6.0' # rubocop:disable Gemspec/RequiredRubyVersion + spec.required_ruby_version = '>= 3.0.2' spec.add_development_dependency 'bundler', '~> 2.3.3' spec.add_development_dependency 'pry-byebug', '~> 3.1' From e21da419301742e51093bb264ada9e464e3eb96b Mon Sep 17 00:00:00 2001 From: Alessandro Fazzi Date: Mon, 3 Jan 2022 22:03:42 +0100 Subject: [PATCH 2/3] Remove some unwanted stdout prints --- lib/wordmove/cli.rb | 11 ++--------- lib/wordmove/doctor/movefile.rb | 2 +- lib/wordmove/doctor/mysql.rb | 2 +- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/lib/wordmove/cli.rb b/lib/wordmove/cli.rb index ff9c42d4..e1e63c83 100644 --- a/lib/wordmove/cli.rb +++ b/lib/wordmove/cli.rb @@ -32,16 +32,9 @@ def ensure_wordpress_options_presence!(cli_options) exit 1 end - def initial_context(cli_options) - cli_options.deep_symbolize_keys! - movefile = Wordmove::Movefile.new(cli_options) - - [cli_options, movefile] - end - - def movefile_from(**cli_options) + def movefile_from(cli_options) ensure_wordpress_options_presence!(cli_options) - Wordmove::Movefile.new(cli_options) + Wordmove::Movefile.new(cli_options, nil, true) rescue MovefileNotFound => e Logger.new($stdout).error(e.message) exit 1 diff --git a/lib/wordmove/doctor/movefile.rb b/lib/wordmove/doctor/movefile.rb index 7fce7e57..aa472a81 100644 --- a/lib/wordmove/doctor/movefile.rb +++ b/lib/wordmove/doctor/movefile.rb @@ -5,7 +5,7 @@ class Movefile attr_reader :movefile, :contents, :root_keys def initialize(cli_options = {}, dir = '.') - @movefile = Wordmove::Movefile.new(cli_options, dir) + @movefile = Wordmove::Movefile.new(cli_options, dir, false) begin @contents = movefile.options diff --git a/lib/wordmove/doctor/mysql.rb b/lib/wordmove/doctor/mysql.rb index 58f58478..a8f25dd7 100644 --- a/lib/wordmove/doctor/mysql.rb +++ b/lib/wordmove/doctor/mysql.rb @@ -7,7 +7,7 @@ def initialize(movefile_name = nil, movefile_dir = '.') @logger = Logger.new($stdout).tap { |l| l.level = Logger::INFO } begin @config = Wordmove::Movefile - .new({ config: movefile_name }, movefile_dir) + .new({ config: movefile_name }, movefile_dir, false) .options[:local][:database] rescue Psych::SyntaxError => e logger.error e.message From 7f5df0e087763e3824123eef06ed71e1f60c2eb9 Mon Sep 17 00:00:00 2001 From: Alessandro Fazzi Date: Tue, 4 Jan 2022 12:56:11 +0100 Subject: [PATCH 3/3] Better message when NoAdapterFound is raised --- lib/wordmove/cli.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/wordmove/cli.rb b/lib/wordmove/cli.rb index e1e63c83..c973e873 100644 --- a/lib/wordmove/cli.rb +++ b/lib/wordmove/cli.rb @@ -122,7 +122,9 @@ def call_pull_organizer_with(**cli_options) cli_options: cli_options ) else - raise NoAdapterFound, 'No valid adapter found.' + raise NoAdapterFound, 'No valid adapter found. It seems like your movefile.yml lacks ' \ + 'an ssh or ftp section for the current environment. ' \ + 'Run `wordmove doctor` for more info' end rescue NoAdapterFound => e Logger.new($stdout).error(e.message) @@ -157,7 +159,9 @@ def call_push_organizer_with(**cli_options) cli_options: cli_options ) else - raise NoAdapterFound, 'No valid adapter found.' + raise NoAdapterFound, 'No valid adapter found. It seems like your movefile.yml lacks ' \ + 'an ssh or ftp section for the current environment. ' \ + 'Run `wordmove doctor` for more info' end rescue NoAdapterFound => e Logger.new($stdout).error(e.message)