From 0e0a94f4839322e6eaf805c06865bc4c89886388 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 26 Jan 2024 03:53:44 -0500 Subject: [PATCH] Handle CLI failure exit status at the top-level script (#28322) --- .rubocop.yml | 3 +- bin/tootctl | 7 +- lib/mastodon/cli/accounts.rb | 152 +++++------------- lib/mastodon/cli/base.rb | 4 + lib/mastodon/cli/cache.rb | 3 +- lib/mastodon/cli/domains.rb | 6 +- lib/mastodon/cli/email_domain_blocks.rb | 10 +- lib/mastodon/cli/emoji.rb | 10 +- lib/mastodon/cli/federation.rb | 16 +- lib/mastodon/cli/feeds.rb | 8 +- lib/mastodon/cli/ip_blocks.rb | 10 +- lib/mastodon/cli/maintenance.rb | 16 +- lib/mastodon/cli/media.rb | 47 ++---- lib/mastodon/cli/progress_helper.rb | 5 +- lib/mastodon/cli/search.rb | 10 +- lib/mastodon/cli/statuses.rb | 5 +- lib/mastodon/cli/upgrade.rb | 6 +- spec/lib/mastodon/cli/accounts_spec.rb | 90 ++++------- spec/lib/mastodon/cli/cache_spec.rb | 3 +- .../mastodon/cli/email_domain_blocks_spec.rb | 6 +- spec/lib/mastodon/cli/feeds_spec.rb | 3 +- spec/lib/mastodon/cli/ip_blocks_spec.rb | 6 +- spec/lib/mastodon/cli/main_spec.rb | 6 +- spec/lib/mastodon/cli/maintenance_spec.rb | 8 +- spec/lib/mastodon/cli/media_spec.rb | 21 +-- spec/lib/mastodon/cli/search_spec.rb | 6 +- spec/lib/mastodon/cli/statuses_spec.rb | 3 +- 27 files changed, 150 insertions(+), 320 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 64021b4cec..6998941144 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -96,12 +96,11 @@ Rails/FilePath: Rails/HttpStatus: EnforcedStyle: numeric -# Reason: Allowed in `tootctl` CLI code and in boot ENV checker +# Reason: Allowed in boot ENV checker # https://docs.rubocop.org/rubocop-rails/cops_rails.html#railsexit Rails/Exit: Exclude: - 'config/boot.rb' - - 'lib/mastodon/cli/*.rb' # Reason: Conflicts with `Lint/UselessMethodDefinition` for inherited controller actions # https://docs.rubocop.org/rubocop-rails/cops_rails.html#railslexicallyscopedactionfilter diff --git a/bin/tootctl b/bin/tootctl index b3311c6b2a..ff64eb2e42 100755 --- a/bin/tootctl +++ b/bin/tootctl @@ -6,8 +6,13 @@ require_relative '../lib/mastodon/cli/main' begin Chewy.strategy(:mastodon) do - Mastodon::CLI::Main.start(ARGV) + Mastodon::CLI::Main.start(ARGV, debug: true) # Enables the script to rescue `Thor::Error` end +rescue Thor::Error => e + Thor::Shell::Color + .new + .say_error(e.message, :red) + exit(1) rescue Interrupt exit(130) end diff --git a/lib/mastodon/cli/accounts.rb b/lib/mastodon/cli/accounts.rb index 9dc65c1477..b5308e2b76 100644 --- a/lib/mastodon/cli/accounts.rb +++ b/lib/mastodon/cli/accounts.rb @@ -39,8 +39,7 @@ module Mastodon::CLI rotate_keys_for_account(Account.find_local(username)) say('OK', :green) else - say('No account(s) given', :red) - exit(1) + fail_with_message 'No account(s) given' end end @@ -74,10 +73,7 @@ module Mastodon::CLI if options[:role] role = UserRole.find_by(name: options[:role]) - if role.nil? - say('Cannot find user role with that name', :red) - exit(1) - end + fail_with_message 'Cannot find user role with that name' if role.nil? role_id = role.id end @@ -114,7 +110,6 @@ module Mastodon::CLI say("New password: #{password}") else report_errors(user.errors) - exit(1) end end @@ -152,18 +147,12 @@ module Mastodon::CLI def modify(username) user = Account.find_local(username)&.user - if user.nil? - say('No user with such username', :red) - exit(1) - end + fail_with_message 'No user with such username' if user.nil? if options[:role] role = UserRole.find_by(name: options[:role]) - if role.nil? - say('Cannot find user role with that name', :red) - exit(1) - end + fail_with_message 'Cannot find user role with that name' if role.nil? user.role_id = role.id elsif options[:remove_role] @@ -185,7 +174,6 @@ module Mastodon::CLI say("New password: #{password}") if options[:reset_password] else report_errors(user.errors) - exit(1) end end @@ -200,27 +188,19 @@ module Mastodon::CLI LONG_DESC def delete(username = nil) if username.present? && options[:email].present? - say('Use username or --email, not both', :red) - exit(1) + fail_with_message 'Use username or --email, not both' elsif username.blank? && options[:email].blank? - say('No username provided', :red) - exit(1) + fail_with_message 'No username provided' end account = nil if username.present? account = Account.find_local(username) - if account.nil? - say('No user with such username', :red) - exit(1) - end + fail_with_message 'No user with such username' if account.nil? else account = Account.left_joins(:user).find_by(user: { email: options[:email] }) - if account.nil? - say('No user with such email', :red) - exit(1) - end + fail_with_message 'No user with such email' if account.nil? end say("Deleting user with #{account.statuses_count} statuses, this might take a while...#{dry_run_mode_suffix}") @@ -243,23 +223,18 @@ module Mastodon::CLI username, domain = from_acct.split('@') from_account = Account.find_remote(username, domain) - if from_account.nil? || from_account.local? - say("No such account (#{from_acct})", :red) - exit(1) - end + fail_with_message "No such account (#{from_acct})" if from_account.nil? || from_account.local? username, domain = to_acct.split('@') to_account = Account.find_remote(username, domain) - if to_account.nil? || to_account.local? - say("No such account (#{to_acct})", :red) - exit(1) - end + fail_with_message "No such account (#{to_acct})" if to_account.nil? || to_account.local? if from_account.public_key != to_account.public_key && !options[:force] - say("Accounts don't have the same public key, might not be duplicates!", :red) - say('Override with --force', :red) - exit(1) + fail_with_message <<~ERROR + Accounts don't have the same public key, might not be duplicates! + Override with --force + ERROR end to_account.merge_with!(from_account) @@ -298,10 +273,7 @@ module Mastodon::CLI def backup(username) account = Account.find_local(username) - if account.nil? - say('No user with such username', :red) - exit(1) - end + fail_with_message 'No user with such username' if account.nil? backup = account.user.backups.create! BackupWorker.perform_async(backup.id) @@ -387,10 +359,7 @@ module Mastodon::CLI user, domain = user.split('@') account = Account.find_remote(user, domain) - if account.nil? - say('No such account', :red) - exit(1) - end + fail_with_message 'No such account' if account.nil? next if dry_run? @@ -405,8 +374,7 @@ module Mastodon::CLI say("OK#{dry_run_mode_suffix}", :green) else - say('No account(s) given', :red) - exit(1) + fail_with_message 'No account(s) given' end end @@ -416,10 +384,7 @@ module Mastodon::CLI def follow(username) target_account = Account.find_local(username) - if target_account.nil? - say('No such account', :red) - exit(1) - end + fail_with_message 'No such account' if target_account.nil? processed, = parallelize_with_progress(Account.local.without_suspended) do |account| FollowService.new.call(account, target_account, bypass_limit: true) @@ -435,10 +400,7 @@ module Mastodon::CLI username, domain = acct.split('@') target_account = Account.find_remote(username, domain) - if target_account.nil? - say('No such account', :red) - exit(1) - end + fail_with_message 'No such account' if target_account.nil? processed, = parallelize_with_progress(target_account.followers.local) do |account| UnfollowService.new.call(account, target_account) @@ -459,17 +421,11 @@ module Mastodon::CLI With the --followers option, the command removes all followers of the account. LONG_DESC def reset_relationships(username) - unless options[:follows] || options[:followers] - say('Please specify either --follows or --followers, or both', :red) - exit(1) - end + fail_with_message 'Please specify either --follows or --followers, or both' unless options[:follows] || options[:followers] account = Account.find_local(username) - if account.nil? - say('No such account', :red) - exit(1) - end + fail_with_message 'No such account' if account.nil? total = 0 total += account.following.reorder(nil).count if options[:follows] @@ -515,6 +471,8 @@ module Mastodon::CLI account identified by its username. LONG_DESC def approve(username = nil) + fail_with_message 'Number must be positive' if options[:number]&.negative? + if options[:all] User.pending.find_each(&:approve!) say('OK', :green) @@ -524,16 +482,10 @@ module Mastodon::CLI elsif username.present? account = Account.find_local(username) - if account.nil? - say('No such account', :red) - exit(1) - end + fail_with_message 'No such account' if account.nil? account.user&.approve! say('OK', :green) - else - say('Number must be positive', :red) if options[:number] - exit(1) end end @@ -587,56 +539,34 @@ module Mastodon::CLI redirects to a different account that the one specified. LONG_DESC def migrate(username) - if options[:replay].present? && options[:target].present? - say('Use --replay or --target, not both', :red) - exit(1) - end + fail_with_message 'Use --replay or --target, not both' if options[:replay].present? && options[:target].present? - if options[:replay].blank? && options[:target].blank? - say('Use either --replay or --target', :red) - exit(1) - end + fail_with_message 'Use either --replay or --target' if options[:replay].blank? && options[:target].blank? account = Account.find_local(username) - if account.nil? - say("No such account: #{username}", :red) - exit(1) - end + fail_with_message "No such account: #{username}" if account.nil? migration = nil if options[:replay] migration = account.migrations.last - if migration.nil? - say('The specified account has not performed any migration', :red) - exit(1) - end + fail_with_message 'The specified account has not performed any migration' if migration.nil? - unless options[:force] || migration.target_account_id == account.moved_to_account_id - say('The specified account is not redirecting to its last migration target. Use --force if you want to replay the migration anyway', :red) - exit(1) - end + fail_with_message 'The specified account is not redirecting to its last migration target. Use --force if you want to replay the migration anyway' unless options[:force] || migration.target_account_id == account.moved_to_account_id end if options[:target] target_account = ResolveAccountService.new.call(options[:target]) - if target_account.nil? - say("The specified target account could not be found: #{options[:target]}", :red) - exit(1) - end + fail_with_message "The specified target account could not be found: #{options[:target]}" if target_account.nil? - unless options[:force] || account.moved_to_account_id.nil? || account.moved_to_account_id == target_account.id - say('The specified account is redirecting to a different target account. Use --force if you want to change the migration target', :red) - exit(1) - end + fail_with_message 'The specified account is redirecting to a different target account. Use --force if you want to change the migration target' unless options[:force] || account.moved_to_account_id.nil? || account.moved_to_account_id == target_account.id begin migration = account.migrations.create!(acct: target_account.acct) rescue ActiveRecord::RecordInvalid => e - say("Error: #{e.message}", :red) - exit(1) + fail_with_message "Error: #{e.message}" end end @@ -648,18 +578,18 @@ module Mastodon::CLI private def report_errors(errors) - errors.each do |error| - say('Failure/Error: ', :red) - say(error.attribute) - say(" #{error.type}", :red) - end + message = errors.map do |error| + <<~STRING + Failure/Error: #{error.attribute} + #{error.type} + STRING + end.join + + fail_with_message message end def rotate_keys_for_account(account, delay = 0) - if account.nil? - say('No such account', :red) - exit(1) - end + fail_with_message 'No such account' if account.nil? old_key = account.private_key new_key = OpenSSL::PKey::RSA.new(2048) diff --git a/lib/mastodon/cli/base.rb b/lib/mastodon/cli/base.rb index 8c222bbb2b..93dec1fb8f 100644 --- a/lib/mastodon/cli/base.rb +++ b/lib/mastodon/cli/base.rb @@ -18,6 +18,10 @@ module Mastodon private + def fail_with_message(message) + raise Thor::Error, message + end + def pastel @pastel ||= Pastel.new end diff --git a/lib/mastodon/cli/cache.rb b/lib/mastodon/cli/cache.rb index e8a2ac1610..f32ab292ee 100644 --- a/lib/mastodon/cli/cache.rb +++ b/lib/mastodon/cli/cache.rb @@ -31,8 +31,7 @@ module Mastodon::CLI recount_status_stats(status) end else - say("Unknown type: #{type}", :red) - exit(1) + fail_with_message "Unknown type: #{type}" end say diff --git a/lib/mastodon/cli/domains.rb b/lib/mastodon/cli/domains.rb index e092497dc9..c247463af5 100644 --- a/lib/mastodon/cli/domains.rb +++ b/lib/mastodon/cli/domains.rb @@ -41,11 +41,9 @@ module Mastodon::CLI # Sanity check on command arguments if options[:limited_federation_mode] && !domains.empty? - say('DOMAIN parameter not supported with --limited-federation-mode', :red) - exit(1) + fail_with_message 'DOMAIN parameter not supported with --limited-federation-mode' elsif domains.empty? && !options[:limited_federation_mode] - say('No domain(s) given', :red) - exit(1) + fail_with_message 'No domain(s) given' end # Build scopes from command arguments diff --git a/lib/mastodon/cli/email_domain_blocks.rb b/lib/mastodon/cli/email_domain_blocks.rb index 022b1dcbb1..6b9107c8ad 100644 --- a/lib/mastodon/cli/email_domain_blocks.rb +++ b/lib/mastodon/cli/email_domain_blocks.rb @@ -30,10 +30,7 @@ module Mastodon::CLI it at the root. LONG_DESC def add(*domains) - if domains.empty? - say('No domain(s) given', :red) - exit(1) - end + fail_with_message 'No domain(s) given' if domains.empty? skipped = 0 processed = 0 @@ -76,10 +73,7 @@ module Mastodon::CLI desc 'remove DOMAIN...', 'Remove e-mail domain blocks' def remove(*domains) - if domains.empty? - say('No domain(s) given', :red) - exit(1) - end + fail_with_message 'No domain(s) given' if domains.empty? skipped = 0 processed = 0 diff --git a/lib/mastodon/cli/emoji.rb b/lib/mastodon/cli/emoji.rb index 912c099124..4a8949de0e 100644 --- a/lib/mastodon/cli/emoji.rb +++ b/lib/mastodon/cli/emoji.rb @@ -86,14 +86,8 @@ module Mastodon::CLI category = CustomEmojiCategory.find_by(name: options[:category]) export_file_name = File.join(path, 'export.tar.gz') - if File.file?(export_file_name) && !options[:overwrite] - say("Archive already exists! Use '--overwrite' to overwrite it!") - exit 1 - end - if category.nil? && options[:category] - say("Unable to find category '#{options[:category]}'!") - exit 1 - end + fail_with_message "Archive already exists! Use '--overwrite' to overwrite it!" if File.file?(export_file_name) && !options[:overwrite] + fail_with_message "Unable to find category '#{options[:category]}'!" if category.nil? && options[:category] File.open(export_file_name, 'wb') do |file| Zlib::GzipWriter.wrap(file) do |gzip| diff --git a/lib/mastodon/cli/federation.rb b/lib/mastodon/cli/federation.rb index 8bb46ecb1a..c738796557 100644 --- a/lib/mastodon/cli/federation.rb +++ b/lib/mastodon/cli/federation.rb @@ -43,10 +43,10 @@ module Mastodon::CLI say('Every deletion notice has been sent! You can safely delete all data and decomission your servers!', :green) end - exit(0) + raise(SystemExit) end - exit(1) unless ask('Type in the domain of the server to confirm:') == Rails.configuration.x.local_domain + fail_with_message 'Domains do not match. Stopping self-destruct initiation.' unless domain_match_confirmed? say(<<~WARNING, :yellow) This operation WILL NOT be reversible. @@ -54,19 +54,25 @@ module Mastodon::CLI The deletion process itself may take a long time, and will be handled by Sidekiq, so do not shut it down until it has finished (you will be able to re-run this command to see the state of the self-destruct process). WARNING - exit(1) if no?('Are you sure you want to proceed?') + fail_with_message 'Operation cancelled. Self-destruct will not begin.' if proceed_prompt_negative? say(<<~INSTRUCTIONS, :green) To switch Mastodon to self-destruct mode, add the following variable to your evironment (e.g. by adding a line to your `.env.production`) and restart all Mastodon processes: SELF_DESTRUCT=#{self_destruct_value} You can re-run this command to see the state of the self-destruct process. INSTRUCTIONS - rescue Interrupt - exit(1) end private + def domain_match_confirmed? + ask('Type in the domain of the server to confirm:') == Rails.configuration.x.local_domain + end + + def proceed_prompt_negative? + no?('Are you sure you want to proceed?') + end + def self_destruct_value Rails .application diff --git a/lib/mastodon/cli/feeds.rb b/lib/mastodon/cli/feeds.rb index 3467dd427c..39affd5e8e 100644 --- a/lib/mastodon/cli/feeds.rb +++ b/lib/mastodon/cli/feeds.rb @@ -27,17 +27,13 @@ module Mastodon::CLI elsif username.present? account = Account.find_local(username) - if account.nil? - say('No such account', :red) - exit(1) - end + fail_with_message 'No such account' if account.nil? PrecomputeFeedService.new.call(account) unless dry_run? say("OK #{dry_run_mode_suffix}", :green, true) else - say('No account(s) given', :red) - exit(1) + fail_with_message 'No account(s) given' end end diff --git a/lib/mastodon/cli/ip_blocks.rb b/lib/mastodon/cli/ip_blocks.rb index d12919c360..100bf7bada 100644 --- a/lib/mastodon/cli/ip_blocks.rb +++ b/lib/mastodon/cli/ip_blocks.rb @@ -20,10 +20,7 @@ module Mastodon::CLI option to overwrite it. LONG_DESC def add(*addresses) - if addresses.empty? - say('No IP(s) given', :red) - exit(1) - end + fail_with_message 'No IP(s) given' if addresses.empty? skipped = 0 processed = 0 @@ -70,10 +67,7 @@ module Mastodon::CLI cover the given IP(s). LONG_DESC def remove(*addresses) - if addresses.empty? - say('No IP(s) given', :red) - exit(1) - end + fail_with_message 'No IP(s) given' if addresses.empty? processed = 0 skipped = 0 diff --git a/lib/mastodon/cli/maintenance.rb b/lib/mastodon/cli/maintenance.rb index a64206065d..9f234e3860 100644 --- a/lib/mastodon/cli/maintenance.rb +++ b/lib/mastodon/cli/maintenance.rb @@ -199,26 +199,24 @@ module Mastodon::CLI def verify_schema_version! if migrator_version < MIN_SUPPORTED_VERSION - say 'Your version of the database schema is too old and is not supported by this script.', :red - say 'Please update to at least Mastodon 3.0.0 before running this script.', :red - exit(1) + fail_with_message <<~ERROR + Your version of the database schema is too old and is not supported by this script. + Please update to at least Mastodon 3.0.0 before running this script. + ERROR elsif migrator_version > MAX_SUPPORTED_VERSION say 'Your version of the database schema is more recent than this script, this may cause unexpected errors.', :yellow - exit(1) unless yes?('Continue anyway? (Yes/No)') + fail_with_message 'Stopping maintenance script because data is more recent than script version.' unless yes?('Continue anyway? (Yes/No)') end end def verify_sidekiq_not_active! - if Sidekiq::ProcessSet.new.any? - say 'It seems Sidekiq is running. All Mastodon processes need to be stopped when using this script.', :red - exit(1) - end + fail_with_message 'It seems Sidekiq is running. All Mastodon processes need to be stopped when using this script.' if Sidekiq::ProcessSet.new.any? end def verify_backup_warning! say 'This task will take a long time to run and is potentially destructive.', :yellow say 'Please make sure to stop Mastodon and have a backup.', :yellow - exit(1) unless yes?('Continue? (Yes/No)') + fail_with_message 'Maintenance process stopped.' unless yes?('Continue? (Yes/No)') end def deduplicate_accounts! diff --git a/lib/mastodon/cli/media.rb b/lib/mastodon/cli/media.rb index a796bb729a..87946e0262 100644 --- a/lib/mastodon/cli/media.rb +++ b/lib/mastodon/cli/media.rb @@ -31,15 +31,9 @@ module Mastodon::CLI following anyone locally are pruned. DESC def remove - if options[:prune_profiles] && options[:remove_headers] - say('--prune-profiles and --remove-headers should not be specified simultaneously', :red, true) - exit(1) - end + fail_with_message '--prune-profiles and --remove-headers should not be specified simultaneously' if options[:prune_profiles] && options[:remove_headers] - if options[:include_follows] && !(options[:prune_profiles] || options[:remove_headers]) - say('--include-follows can only be used with --prune-profiles or --remove-headers', :red, true) - exit(1) - end + fail_with_message '--include-follows can only be used with --prune-profiles or --remove-headers' if options[:include_follows] && !(options[:prune_profiles] || options[:remove_headers]) time_ago = options[:days].days.ago if options[:prune_profiles] || options[:remove_headers] @@ -156,11 +150,9 @@ module Mastodon::CLI end end when :fog - say('The fog storage driver is not supported for this operation at this time', :red) - exit(1) + fail_with_message 'The fog storage driver is not supported for this operation at this time' when :azure - say('The azure storage driver is not supported for this operation at this time', :red) - exit(1) + fail_with_message 'The azure storage driver is not supported for this operation at this time' when :filesystem require 'find' @@ -254,10 +246,7 @@ module Mastodon::CLI username, domain = options[:account].split('@') account = Account.find_remote(username, domain) - if account.nil? - say('No such account', :red) - exit(1) - end + fail_with_message 'No such account' if account.nil? scope = MediaAttachment.where(account_id: account.id) elsif options[:domain] @@ -265,8 +254,7 @@ module Mastodon::CLI elsif options[:days].present? scope = MediaAttachment.remote else - say('Specify the source of media attachments', :red) - exit(1) + fail_with_message 'Specify the source of media attachments' end scope = scope.where('media_attachments.id > ?', Mastodon::Snowflake.id_at(options[:days].days.ago, with_random: false)) if options[:days].present? @@ -306,38 +294,25 @@ module Mastodon::CLI path_segments = path.split('/')[2..] path_segments.delete('cache') - unless VALID_PATH_SEGMENTS_SIZE.include?(path_segments.size) - say('Not a media URL', :red) - exit(1) - end + fail_with_message 'Not a media URL' unless VALID_PATH_SEGMENTS_SIZE.include?(path_segments.size) model_name = path_segments.first.classify record_id = path_segments[2..-2].join.to_i - unless PRELOAD_MODEL_WHITELIST.include?(model_name) - say("Cannot find corresponding model: #{model_name}", :red) - exit(1) - end + fail_with_message "Cannot find corresponding model: #{model_name}" unless PRELOAD_MODEL_WHITELIST.include?(model_name) record = model_name.constantize.find_by(id: record_id) record = record.status if record.respond_to?(:status) - unless record - say('Cannot find corresponding record', :red) - exit(1) - end + fail_with_message 'Cannot find corresponding record' unless record display_url = ActivityPub::TagManager.instance.url_for(record) - if display_url.blank? - say('No public URL for this type of record', :red) - exit(1) - end + fail_with_message 'No public URL for this type of record' if display_url.blank? say(display_url, :blue) rescue Addressable::URI::InvalidURIError - say('Invalid URL', :red) - exit(1) + fail_with_message 'Invalid URL' end private diff --git a/lib/mastodon/cli/progress_helper.rb b/lib/mastodon/cli/progress_helper.rb index 1fa2745c18..4fcc47a7f7 100644 --- a/lib/mastodon/cli/progress_helper.rb +++ b/lib/mastodon/cli/progress_helper.rb @@ -25,10 +25,7 @@ module Mastodon::CLI end def parallelize_with_progress(scope) - if options[:concurrency] < 1 - say('Cannot run with this concurrency setting, must be at least 1', :red) - exit(1) - end + fail_with_message 'Cannot run with this concurrency setting, must be at least 1' if options[:concurrency] < 1 reset_connection_pools! diff --git a/lib/mastodon/cli/search.rb b/lib/mastodon/cli/search.rb index 77c455f049..5901c07776 100644 --- a/lib/mastodon/cli/search.rb +++ b/lib/mastodon/cli/search.rb @@ -110,17 +110,11 @@ module Mastodon::CLI end def verify_deploy_concurrency! - return unless options[:concurrency] < 1 - - say('Cannot run with this concurrency setting, must be at least 1', :red) - exit(1) + fail_with_message 'Cannot run with this concurrency setting, must be at least 1' if options[:concurrency] < 1 end def verify_deploy_batch_size! - return unless options[:batch_size] < 1 - - say('Cannot run with this batch_size setting, must be at least 1', :red) - exit(1) + fail_with_message 'Cannot run with this batch_size setting, must be at least 1' if options[:batch_size] < 1 end def progress_output_options diff --git a/lib/mastodon/cli/statuses.rb b/lib/mastodon/cli/statuses.rb index 48d76e0288..57b03c941c 100644 --- a/lib/mastodon/cli/statuses.rb +++ b/lib/mastodon/cli/statuses.rb @@ -26,10 +26,7 @@ module Mastodon::CLI indices before commencing, and removes them afterward. LONG_DESC def remove - if options[:batch_size] < 1 - say('Cannot run with this batch_size setting, must be at least 1', :red) - exit(1) - end + fail_with_message 'Cannot run with this batch_size setting, must be at least 1' if options[:batch_size] < 1 remove_statuses vacuum_and_analyze_statuses diff --git a/lib/mastodon/cli/upgrade.rb b/lib/mastodon/cli/upgrade.rb index cf83986844..2cb5105794 100644 --- a/lib/mastodon/cli/upgrade.rb +++ b/lib/mastodon/cli/upgrade.rb @@ -103,13 +103,11 @@ module Mastodon::CLI end def upgrade_storage_fog(_progress, _attachment, _style) - say('The fog storage driver is not supported for this operation at this time', :red) - exit(1) + fail_with_message 'The fog storage driver is not supported for this operation at this time' end def upgrade_storage_azure(_progress, _attachment, _style) - say('The azure storage driver is not supported for this operation at this time', :red) - exit(1) + fail_with_message 'The azure storage driver is not supported for this operation at this time' end def upgrade_storage_filesystem(progress, attachment, style) diff --git a/spec/lib/mastodon/cli/accounts_spec.rb b/spec/lib/mastodon/cli/accounts_spec.rb index 26ad983bbc..98be2b2027 100644 --- a/spec/lib/mastodon/cli/accounts_spec.rb +++ b/spec/lib/mastodon/cli/accounts_spec.rb @@ -65,8 +65,7 @@ describe Mastodon::CLI::Accounts do it 'exits with an error message' do expect { subject } - .to output_results('Failure/Error: email') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, %r{Failure/Error: email}) end end end @@ -127,8 +126,7 @@ describe Mastodon::CLI::Accounts do it 'exits with an error message indicating the role name was not found' do expect { subject } - .to output_results('Cannot find user role with that name') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, 'Cannot find user role with that name') end end end @@ -191,8 +189,7 @@ describe Mastodon::CLI::Accounts do it 'exits with an error message indicating the user was not found' do expect { subject } - .to output_results('No user with such username') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, 'No user with such username') end end @@ -214,8 +211,7 @@ describe Mastodon::CLI::Accounts do it 'exits with an error message indicating the role was not found' do expect { subject } - .to output_results('Cannot find user role with that name') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, 'Cannot find user role with that name') end end @@ -364,8 +360,7 @@ describe Mastodon::CLI::Accounts do it 'exits with an error message' do expect { subject } - .to output_results('Failure/Error: email') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, %r{Failure/Error: email}) end end end @@ -387,16 +382,14 @@ describe Mastodon::CLI::Accounts do it 'exits with an error message indicating that only one should be used' do expect { subject } - .to output_results('Use username or --email, not both') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, 'Use username or --email, not both') end end context 'when neither username nor --email are provided' do it 'exits with an error message indicating that no username was provided' do expect { subject } - .to output_results('No username provided') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, 'No username provided') end end @@ -425,8 +418,7 @@ describe Mastodon::CLI::Accounts do it 'exits with an error message indicating that no user was found' do expect { subject } - .to output_results('No user with such username') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, 'No user with such username') end end end @@ -458,8 +450,7 @@ describe Mastodon::CLI::Accounts do it 'exits with an error message indicating that no user was found' do expect { subject } - .to output_results('No user with such email') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, 'No user with such email') end end end @@ -511,8 +502,7 @@ describe Mastodon::CLI::Accounts do it 'exits with an error message indicating that the number must be positive' do expect { subject } - .to output_results('Number must be positive') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, 'Number must be positive') end end @@ -545,8 +535,7 @@ describe Mastodon::CLI::Accounts do it 'exits with an error message indicating that no such account was found' do expect { subject } - .to output_results('No such account') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, 'No such account') end end end @@ -560,8 +549,7 @@ describe Mastodon::CLI::Accounts do it 'exits with an error message indicating that no account with the given username was found' do expect { subject } - .to output_results('No such account') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, 'No such account') end end @@ -596,8 +584,7 @@ describe Mastodon::CLI::Accounts do it 'exits with an error message indicating that no account with the given username was found' do expect { subject } - .to output_results('No such account') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, 'No such account') end end @@ -634,8 +621,7 @@ describe Mastodon::CLI::Accounts do it 'exits with an error message indicating that there is no such account' do expect { subject } - .to output_results('No user with such username') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, 'No user with such username') end end @@ -795,8 +781,7 @@ describe Mastodon::CLI::Accounts do allow(Account).to receive(:find_remote).with(account_example_com_b.username, account_example_com_b.domain).and_return(nil) expect { subject } - .to output_results('No such account') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, 'No such account') end end @@ -892,8 +877,7 @@ describe Mastodon::CLI::Accounts do context 'when neither a list of accts nor options are provided' do it 'exits with an error message' do expect { subject } - .to output_results('No account(s) given') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, 'No account(s) given') end end end @@ -904,8 +888,7 @@ describe Mastodon::CLI::Accounts do context 'when neither username nor --all option are given' do it 'exits with an error message' do expect { subject } - .to output_results('No account(s) given') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, 'No account(s) given') end end @@ -940,8 +923,7 @@ describe Mastodon::CLI::Accounts do it 'exits with an error message when the specified username is not found' do expect { subject } - .to output_results('No such account') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, 'No such account') end end @@ -980,8 +962,7 @@ describe Mastodon::CLI::Accounts do shared_examples 'an account not found' do |acct| it 'exits with an error message indicating that there is no such account' do expect { subject } - .to output_results("No such account (#{acct})") - .and raise_error(SystemExit) + .to raise_error(Thor::Error, "No such account (#{acct})") end end @@ -1031,8 +1012,7 @@ describe Mastodon::CLI::Accounts do it 'exits with an error message indicating that the accounts do not have the same pub key' do expect { subject } - .to output_results("Accounts don't have the same public key, might not be duplicates!\nOverride with --force") - .and raise_error(SystemExit) + .to raise_error(Thor::Error, "Accounts don't have the same public key, might not be duplicates!\nOverride with --force\n") end context 'with --force option' do @@ -1200,8 +1180,7 @@ describe Mastodon::CLI::Accounts do context 'when no option is given' do it 'exits with an error message indicating that at least one option is required' do expect { subject } - .to output_results('Please specify either --follows or --followers, or both') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, 'Please specify either --follows or --followers, or both') end end @@ -1211,8 +1190,7 @@ describe Mastodon::CLI::Accounts do it 'exits with an error message indicating that there is no such account' do expect { subject } - .to output_results('No such account') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, 'No such account') end end @@ -1368,16 +1346,14 @@ describe Mastodon::CLI::Accounts do it 'exits with an error message indicating that using both options is not possible' do expect { subject } - .to output_results('Use --replay or --target, not both') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, 'Use --replay or --target, not both') end end context 'when no option is given' do it 'exits with an error message indicating that at least one option must be used' do expect { subject } - .to output_results('Use either --replay or --target') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, 'Use either --replay or --target') end end @@ -1387,8 +1363,7 @@ describe Mastodon::CLI::Accounts do it 'exits with an error message indicating that there is no such account' do expect { subject } - .to output_results("No such account: #{arguments.first}") - .and raise_error(SystemExit) + .to raise_error(Thor::Error, "No such account: #{arguments.first}") end end @@ -1398,8 +1373,7 @@ describe Mastodon::CLI::Accounts do context 'when the specified account has no previous migrations' do it 'exits with an error message indicating that the given account has no previous migrations' do expect { subject } - .to output_results('The specified account has not performed any migration') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, 'The specified account has not performed any migration') end end @@ -1421,8 +1395,7 @@ describe Mastodon::CLI::Accounts do it 'exits with an error message' do expect { subject } - .to output_results('The specified account is not redirecting to its last migration target. Use --force if you want to replay the migration anyway') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, 'The specified account is not redirecting to its last migration target. Use --force if you want to replay the migration anyway') end end @@ -1449,8 +1422,7 @@ describe Mastodon::CLI::Accounts do it 'exits with an error message indicating that there is no such account' do expect { subject } - .to output_results("The specified target account could not be found: #{options[:target]}") - .and raise_error(SystemExit) + .to raise_error(Thor::Error, "The specified target account could not be found: #{options[:target]}") end end @@ -1474,8 +1446,7 @@ describe Mastodon::CLI::Accounts do context 'when the migration record is invalid' do it 'exits with an error indicating that the validation failed' do expect { subject } - .to output_results('Error: Validation failed') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, /Error: Validation failed/) end end @@ -1486,8 +1457,7 @@ describe Mastodon::CLI::Accounts do it 'exits with an error message' do expect { subject } - .to output_results('The specified account is redirecting to a different target account. Use --force if you want to change the migration target') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, 'The specified account is redirecting to a different target account. Use --force if you want to change the migration target') end end diff --git a/spec/lib/mastodon/cli/cache_spec.rb b/spec/lib/mastodon/cli/cache_spec.rb index b1515801eb..247a14f9e2 100644 --- a/spec/lib/mastodon/cli/cache_spec.rb +++ b/spec/lib/mastodon/cli/cache_spec.rb @@ -64,8 +64,7 @@ describe Mastodon::CLI::Cache do it 'Exits with an error message' do expect { subject } - .to output_results('Unknown') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, /Unknown/) end end end diff --git a/spec/lib/mastodon/cli/email_domain_blocks_spec.rb b/spec/lib/mastodon/cli/email_domain_blocks_spec.rb index 13deb05b6c..55e3da0bb8 100644 --- a/spec/lib/mastodon/cli/email_domain_blocks_spec.rb +++ b/spec/lib/mastodon/cli/email_domain_blocks_spec.rb @@ -35,8 +35,7 @@ describe Mastodon::CLI::EmailDomainBlocks do context 'without any options' do it 'warns about usage and exits' do expect { subject } - .to output_results('No domain(s) given') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, 'No domain(s) given') end end @@ -72,8 +71,7 @@ describe Mastodon::CLI::EmailDomainBlocks do context 'without any options' do it 'warns about usage and exits' do expect { subject } - .to output_results('No domain(s) given') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, 'No domain(s) given') end end diff --git a/spec/lib/mastodon/cli/feeds_spec.rb b/spec/lib/mastodon/cli/feeds_spec.rb index 1997980527..420cb3d587 100644 --- a/spec/lib/mastodon/cli/feeds_spec.rb +++ b/spec/lib/mastodon/cli/feeds_spec.rb @@ -42,8 +42,7 @@ describe Mastodon::CLI::Feeds do it 'displays an error and exits' do expect { subject } - .to output_results('No such account') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, 'No such account') end end end diff --git a/spec/lib/mastodon/cli/ip_blocks_spec.rb b/spec/lib/mastodon/cli/ip_blocks_spec.rb index 82be10813e..d44b1b9fe4 100644 --- a/spec/lib/mastodon/cli/ip_blocks_spec.rb +++ b/spec/lib/mastodon/cli/ip_blocks_spec.rb @@ -144,8 +144,7 @@ describe Mastodon::CLI::IpBlocks do it 'exits with an error message' do expect { subject } - .to output_results('No IP(s) given') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, 'No IP(s) given') end end end @@ -235,8 +234,7 @@ describe Mastodon::CLI::IpBlocks do context 'when no IP address is provided' do it 'exits with an error message' do expect { subject } - .to output_results('No IP(s) given') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, 'No IP(s) given') end end end diff --git a/spec/lib/mastodon/cli/main_spec.rb b/spec/lib/mastodon/cli/main_spec.rb index 081cd2dd47..99d770a81d 100644 --- a/spec/lib/mastodon/cli/main_spec.rb +++ b/spec/lib/mastodon/cli/main_spec.rb @@ -104,9 +104,9 @@ describe Mastodon::CLI::Main do answer_hostname_incorrectly end - it 'exits silently' do + it 'exits with mismatch error message' do expect { subject } - .to raise_error(SystemExit) + .to raise_error(Thor::Error, /Domains do not match/) end end @@ -119,7 +119,7 @@ describe Mastodon::CLI::Main do it 'passes first step but stops before instructions' do expect { subject } .to output_results('operation WILL NOT') - .and raise_error(SystemExit) + .and raise_error(Thor::Error, /Self-destruct will not begin/) end end diff --git a/spec/lib/mastodon/cli/maintenance_spec.rb b/spec/lib/mastodon/cli/maintenance_spec.rb index ca492bbf69..cde25d39ed 100644 --- a/spec/lib/mastodon/cli/maintenance_spec.rb +++ b/spec/lib/mastodon/cli/maintenance_spec.rb @@ -22,8 +22,7 @@ describe Mastodon::CLI::Maintenance do it 'Exits with error message' do expect { subject } - .to output_results('is too old') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, /is too old/) end end @@ -36,7 +35,7 @@ describe Mastodon::CLI::Maintenance do it 'Exits with error message' do expect { subject } .to output_results('more recent') - .and raise_error(SystemExit) + .and raise_error(Thor::Error, /more recent/) end end @@ -48,8 +47,7 @@ describe Mastodon::CLI::Maintenance do it 'Exits with error message' do expect { subject } - .to output_results('Sidekiq is running') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, /Sidekiq is running/) end end diff --git a/spec/lib/mastodon/cli/media_spec.rb b/spec/lib/mastodon/cli/media_spec.rb index 10005107aa..ecc7101b6c 100644 --- a/spec/lib/mastodon/cli/media_spec.rb +++ b/spec/lib/mastodon/cli/media_spec.rb @@ -20,8 +20,7 @@ describe Mastodon::CLI::Media do it 'warns about usage and exits' do expect { subject } - .to output_results('--prune-profiles and --remove-headers should not be specified simultaneously') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, '--prune-profiles and --remove-headers should not be specified simultaneously') end end @@ -30,8 +29,7 @@ describe Mastodon::CLI::Media do it 'warns about usage and exits' do expect { subject } - .to output_results('--include-follows can only be used with --prune-profiles or --remove-headers') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, '--include-follows can only be used with --prune-profiles or --remove-headers') end end @@ -98,8 +96,7 @@ describe Mastodon::CLI::Media do it 'warns about url and exits' do expect { subject } - .to output_results('Not a media URL') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, 'Not a media URL') end end @@ -121,8 +118,7 @@ describe Mastodon::CLI::Media do context 'without any options' do it 'warns about usage and exits' do expect { subject } - .to output_results('Specify the source') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, /Specify the source/) end end @@ -147,8 +143,7 @@ describe Mastodon::CLI::Media do it 'warns about usage and exits' do expect { subject } - .to output_results('No such account') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, 'No such account') end end @@ -221,8 +216,7 @@ describe Mastodon::CLI::Media do it 'warns about usage and exits' do expect { subject } - .to output_results('azure storage driver is not supported') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, /azure storage driver is not supported/) end end @@ -233,8 +227,7 @@ describe Mastodon::CLI::Media do it 'warns about usage and exits' do expect { subject } - .to output_results('fog storage driver is not supported') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, /fog storage driver is not supported/) end end diff --git a/spec/lib/mastodon/cli/search_spec.rb b/spec/lib/mastodon/cli/search_spec.rb index cb0c80c11d..8cce2c6ee2 100644 --- a/spec/lib/mastodon/cli/search_spec.rb +++ b/spec/lib/mastodon/cli/search_spec.rb @@ -20,8 +20,7 @@ describe Mastodon::CLI::Search do it 'Exits with error message' do expect { subject } - .to output_results('this concurrency setting') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, /this concurrency setting/) end end @@ -30,8 +29,7 @@ describe Mastodon::CLI::Search do it 'Exits with error message' do expect { subject } - .to output_results('this batch_size setting') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, /this batch_size setting/) end end diff --git a/spec/lib/mastodon/cli/statuses_spec.rb b/spec/lib/mastodon/cli/statuses_spec.rb index 63d494bbb6..161b7c02bb 100644 --- a/spec/lib/mastodon/cli/statuses_spec.rb +++ b/spec/lib/mastodon/cli/statuses_spec.rb @@ -20,8 +20,7 @@ describe Mastodon::CLI::Statuses do it 'exits with error message' do expect { subject } - .to output_results('Cannot run') - .and raise_error(SystemExit) + .to raise_error(Thor::Error, /Cannot run/) end end