Handle CLI failure exit status at the top-level script (#28322)
This commit is contained in:
		
					parent
					
						
							
								881e8c113c
							
						
					
				
			
			
				commit
				
					
						0e0a94f483
					
				
			
		
					 27 changed files with 150 additions and 320 deletions
				
			
		|  | @ -96,12 +96,11 @@ Rails/FilePath: | ||||||
| Rails/HttpStatus: | Rails/HttpStatus: | ||||||
|   EnforcedStyle: numeric |   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 | # https://docs.rubocop.org/rubocop-rails/cops_rails.html#railsexit | ||||||
| Rails/Exit: | Rails/Exit: | ||||||
|   Exclude: |   Exclude: | ||||||
|     - 'config/boot.rb' |     - 'config/boot.rb' | ||||||
|     - 'lib/mastodon/cli/*.rb' |  | ||||||
| 
 | 
 | ||||||
| # Reason: Conflicts with `Lint/UselessMethodDefinition` for inherited controller actions | # Reason: Conflicts with `Lint/UselessMethodDefinition` for inherited controller actions | ||||||
| # https://docs.rubocop.org/rubocop-rails/cops_rails.html#railslexicallyscopedactionfilter | # https://docs.rubocop.org/rubocop-rails/cops_rails.html#railslexicallyscopedactionfilter | ||||||
|  |  | ||||||
|  | @ -6,8 +6,13 @@ require_relative '../lib/mastodon/cli/main' | ||||||
| 
 | 
 | ||||||
| begin | begin | ||||||
|   Chewy.strategy(:mastodon) do |   Chewy.strategy(:mastodon) do | ||||||
|     Mastodon::CLI::Main.start(ARGV) |     Mastodon::CLI::Main.start(ARGV, debug: true) # Enables the script to rescue `Thor::Error` | ||||||
|   end |   end | ||||||
|  | rescue Thor::Error => e | ||||||
|  |   Thor::Shell::Color | ||||||
|  |     .new | ||||||
|  |     .say_error(e.message, :red) | ||||||
|  |   exit(1) | ||||||
| rescue Interrupt | rescue Interrupt | ||||||
|   exit(130) |   exit(130) | ||||||
| end | end | ||||||
|  |  | ||||||
|  | @ -39,8 +39,7 @@ module Mastodon::CLI | ||||||
|         rotate_keys_for_account(Account.find_local(username)) |         rotate_keys_for_account(Account.find_local(username)) | ||||||
|         say('OK', :green) |         say('OK', :green) | ||||||
|       else |       else | ||||||
|         say('No account(s) given', :red) |         fail_with_message 'No account(s) given' | ||||||
|         exit(1) |  | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  | @ -74,10 +73,7 @@ module Mastodon::CLI | ||||||
|       if options[:role] |       if options[:role] | ||||||
|         role = UserRole.find_by(name: options[:role]) |         role = UserRole.find_by(name: options[:role]) | ||||||
| 
 | 
 | ||||||
|         if role.nil? |         fail_with_message 'Cannot find user role with that name' if role.nil? | ||||||
|           say('Cannot find user role with that name', :red) |  | ||||||
|           exit(1) |  | ||||||
|         end |  | ||||||
| 
 | 
 | ||||||
|         role_id = role.id |         role_id = role.id | ||||||
|       end |       end | ||||||
|  | @ -114,7 +110,6 @@ module Mastodon::CLI | ||||||
|         say("New password: #{password}") |         say("New password: #{password}") | ||||||
|       else |       else | ||||||
|         report_errors(user.errors) |         report_errors(user.errors) | ||||||
|         exit(1) |  | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  | @ -152,18 +147,12 @@ module Mastodon::CLI | ||||||
|     def modify(username) |     def modify(username) | ||||||
|       user = Account.find_local(username)&.user |       user = Account.find_local(username)&.user | ||||||
| 
 | 
 | ||||||
|       if user.nil? |       fail_with_message 'No user with such username' if user.nil? | ||||||
|         say('No user with such username', :red) |  | ||||||
|         exit(1) |  | ||||||
|       end |  | ||||||
| 
 | 
 | ||||||
|       if options[:role] |       if options[:role] | ||||||
|         role = UserRole.find_by(name: options[:role]) |         role = UserRole.find_by(name: options[:role]) | ||||||
| 
 | 
 | ||||||
|         if role.nil? |         fail_with_message 'Cannot find user role with that name' if role.nil? | ||||||
|           say('Cannot find user role with that name', :red) |  | ||||||
|           exit(1) |  | ||||||
|         end |  | ||||||
| 
 | 
 | ||||||
|         user.role_id = role.id |         user.role_id = role.id | ||||||
|       elsif options[:remove_role] |       elsif options[:remove_role] | ||||||
|  | @ -185,7 +174,6 @@ module Mastodon::CLI | ||||||
|         say("New password: #{password}") if options[:reset_password] |         say("New password: #{password}") if options[:reset_password] | ||||||
|       else |       else | ||||||
|         report_errors(user.errors) |         report_errors(user.errors) | ||||||
|         exit(1) |  | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  | @ -200,27 +188,19 @@ module Mastodon::CLI | ||||||
|     LONG_DESC |     LONG_DESC | ||||||
|     def delete(username = nil) |     def delete(username = nil) | ||||||
|       if username.present? && options[:email].present? |       if username.present? && options[:email].present? | ||||||
|         say('Use username or --email, not both', :red) |         fail_with_message  'Use username or --email, not both' | ||||||
|         exit(1) |  | ||||||
|       elsif username.blank? && options[:email].blank? |       elsif username.blank? && options[:email].blank? | ||||||
|         say('No username provided', :red) |         fail_with_message 'No username provided' | ||||||
|         exit(1) |  | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       account = nil |       account = nil | ||||||
| 
 | 
 | ||||||
|       if username.present? |       if username.present? | ||||||
|         account = Account.find_local(username) |         account = Account.find_local(username) | ||||||
|         if account.nil? |         fail_with_message 'No user with such username' if account.nil? | ||||||
|           say('No user with such username', :red) |  | ||||||
|           exit(1) |  | ||||||
|         end |  | ||||||
|       else |       else | ||||||
|         account = Account.left_joins(:user).find_by(user: { email: options[:email] }) |         account = Account.left_joins(:user).find_by(user: { email: options[:email] }) | ||||||
|         if account.nil? |         fail_with_message 'No user with such email' if account.nil? | ||||||
|           say('No user with such email', :red) |  | ||||||
|           exit(1) |  | ||||||
|         end |  | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       say("Deleting user with #{account.statuses_count} statuses, this might take a while...#{dry_run_mode_suffix}") |       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('@') |       username, domain = from_acct.split('@') | ||||||
|       from_account = Account.find_remote(username, domain) |       from_account = Account.find_remote(username, domain) | ||||||
| 
 | 
 | ||||||
|       if from_account.nil? || from_account.local? |       fail_with_message "No such account (#{from_acct})" if from_account.nil? || from_account.local? | ||||||
|         say("No such account (#{from_acct})", :red) |  | ||||||
|         exit(1) |  | ||||||
|       end |  | ||||||
| 
 | 
 | ||||||
|       username, domain = to_acct.split('@') |       username, domain = to_acct.split('@') | ||||||
|       to_account = Account.find_remote(username, domain) |       to_account = Account.find_remote(username, domain) | ||||||
| 
 | 
 | ||||||
|       if to_account.nil? || to_account.local? |       fail_with_message "No such account (#{to_acct})" if to_account.nil? || to_account.local? | ||||||
|         say("No such account (#{to_acct})", :red) |  | ||||||
|         exit(1) |  | ||||||
|       end |  | ||||||
| 
 | 
 | ||||||
|       if from_account.public_key != to_account.public_key && !options[:force] |       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) |         fail_with_message <<~ERROR | ||||||
|         say('Override with --force', :red) |           Accounts don't have the same public key, might not be duplicates! | ||||||
|         exit(1) |           Override with --force | ||||||
|  |         ERROR | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       to_account.merge_with!(from_account) |       to_account.merge_with!(from_account) | ||||||
|  | @ -298,10 +273,7 @@ module Mastodon::CLI | ||||||
|     def backup(username) |     def backup(username) | ||||||
|       account = Account.find_local(username) |       account = Account.find_local(username) | ||||||
| 
 | 
 | ||||||
|       if account.nil? |       fail_with_message 'No user with such username' if account.nil? | ||||||
|         say('No user with such username', :red) |  | ||||||
|         exit(1) |  | ||||||
|       end |  | ||||||
| 
 | 
 | ||||||
|       backup = account.user.backups.create! |       backup = account.user.backups.create! | ||||||
|       BackupWorker.perform_async(backup.id) |       BackupWorker.perform_async(backup.id) | ||||||
|  | @ -387,10 +359,7 @@ module Mastodon::CLI | ||||||
|           user, domain = user.split('@') |           user, domain = user.split('@') | ||||||
|           account = Account.find_remote(user, domain) |           account = Account.find_remote(user, domain) | ||||||
| 
 | 
 | ||||||
|           if account.nil? |           fail_with_message 'No such account' if account.nil? | ||||||
|             say('No such account', :red) |  | ||||||
|             exit(1) |  | ||||||
|           end |  | ||||||
| 
 | 
 | ||||||
|           next if dry_run? |           next if dry_run? | ||||||
| 
 | 
 | ||||||
|  | @ -405,8 +374,7 @@ module Mastodon::CLI | ||||||
| 
 | 
 | ||||||
|         say("OK#{dry_run_mode_suffix}", :green) |         say("OK#{dry_run_mode_suffix}", :green) | ||||||
|       else |       else | ||||||
|         say('No account(s) given', :red) |         fail_with_message 'No account(s) given' | ||||||
|         exit(1) |  | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  | @ -416,10 +384,7 @@ module Mastodon::CLI | ||||||
|     def follow(username) |     def follow(username) | ||||||
|       target_account = Account.find_local(username) |       target_account = Account.find_local(username) | ||||||
| 
 | 
 | ||||||
|       if target_account.nil? |       fail_with_message 'No such account' if target_account.nil? | ||||||
|         say('No such account', :red) |  | ||||||
|         exit(1) |  | ||||||
|       end |  | ||||||
| 
 | 
 | ||||||
|       processed, = parallelize_with_progress(Account.local.without_suspended) do |account| |       processed, = parallelize_with_progress(Account.local.without_suspended) do |account| | ||||||
|         FollowService.new.call(account, target_account, bypass_limit: true) |         FollowService.new.call(account, target_account, bypass_limit: true) | ||||||
|  | @ -435,10 +400,7 @@ module Mastodon::CLI | ||||||
|       username, domain = acct.split('@') |       username, domain = acct.split('@') | ||||||
|       target_account = Account.find_remote(username, domain) |       target_account = Account.find_remote(username, domain) | ||||||
| 
 | 
 | ||||||
|       if target_account.nil? |       fail_with_message 'No such account' if target_account.nil? | ||||||
|         say('No such account', :red) |  | ||||||
|         exit(1) |  | ||||||
|       end |  | ||||||
| 
 | 
 | ||||||
|       processed, = parallelize_with_progress(target_account.followers.local) do |account| |       processed, = parallelize_with_progress(target_account.followers.local) do |account| | ||||||
|         UnfollowService.new.call(account, target_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. |       With the --followers option, the command removes all followers of the account. | ||||||
|     LONG_DESC |     LONG_DESC | ||||||
|     def reset_relationships(username) |     def reset_relationships(username) | ||||||
|       unless options[:follows] || options[:followers] |       fail_with_message 'Please specify either --follows or --followers, or both' unless options[:follows] || options[:followers] | ||||||
|         say('Please specify either --follows or --followers, or both', :red) |  | ||||||
|         exit(1) |  | ||||||
|       end |  | ||||||
| 
 | 
 | ||||||
|       account = Account.find_local(username) |       account = Account.find_local(username) | ||||||
| 
 | 
 | ||||||
|       if account.nil? |       fail_with_message 'No such account' if account.nil? | ||||||
|         say('No such account', :red) |  | ||||||
|         exit(1) |  | ||||||
|       end |  | ||||||
| 
 | 
 | ||||||
|       total     = 0 |       total     = 0 | ||||||
|       total    += account.following.reorder(nil).count if options[:follows] |       total    += account.following.reorder(nil).count if options[:follows] | ||||||
|  | @ -515,6 +471,8 @@ module Mastodon::CLI | ||||||
|       account identified by its username. |       account identified by its username. | ||||||
|     LONG_DESC |     LONG_DESC | ||||||
|     def approve(username = nil) |     def approve(username = nil) | ||||||
|  |       fail_with_message 'Number must be positive' if options[:number]&.negative? | ||||||
|  | 
 | ||||||
|       if options[:all] |       if options[:all] | ||||||
|         User.pending.find_each(&:approve!) |         User.pending.find_each(&:approve!) | ||||||
|         say('OK', :green) |         say('OK', :green) | ||||||
|  | @ -524,16 +482,10 @@ module Mastodon::CLI | ||||||
|       elsif username.present? |       elsif username.present? | ||||||
|         account = Account.find_local(username) |         account = Account.find_local(username) | ||||||
| 
 | 
 | ||||||
|         if account.nil? |         fail_with_message 'No such account' if account.nil? | ||||||
|           say('No such account', :red) |  | ||||||
|           exit(1) |  | ||||||
|         end |  | ||||||
| 
 | 
 | ||||||
|         account.user&.approve! |         account.user&.approve! | ||||||
|         say('OK', :green) |         say('OK', :green) | ||||||
|       else |  | ||||||
|         say('Number must be positive', :red) if options[:number] |  | ||||||
|         exit(1) |  | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  | @ -587,56 +539,34 @@ module Mastodon::CLI | ||||||
|       redirects to a different account that the one specified. |       redirects to a different account that the one specified. | ||||||
|     LONG_DESC |     LONG_DESC | ||||||
|     def migrate(username) |     def migrate(username) | ||||||
|       if options[:replay].present? && options[:target].present? |       fail_with_message 'Use --replay or --target, not both' if options[:replay].present? && options[:target].present? | ||||||
|         say('Use --replay or --target, not both', :red) |  | ||||||
|         exit(1) |  | ||||||
|       end |  | ||||||
| 
 | 
 | ||||||
|       if options[:replay].blank? && options[:target].blank? |       fail_with_message 'Use either --replay or --target' if options[:replay].blank? && options[:target].blank? | ||||||
|         say('Use either --replay or --target', :red) |  | ||||||
|         exit(1) |  | ||||||
|       end |  | ||||||
| 
 | 
 | ||||||
|       account = Account.find_local(username) |       account = Account.find_local(username) | ||||||
| 
 | 
 | ||||||
|       if account.nil? |       fail_with_message "No such account: #{username}" if account.nil? | ||||||
|         say("No such account: #{username}", :red) |  | ||||||
|         exit(1) |  | ||||||
|       end |  | ||||||
| 
 | 
 | ||||||
|       migration = nil |       migration = nil | ||||||
| 
 | 
 | ||||||
|       if options[:replay] |       if options[:replay] | ||||||
|         migration = account.migrations.last |         migration = account.migrations.last | ||||||
|         if migration.nil? |         fail_with_message 'The specified account has not performed any migration' if migration.nil? | ||||||
|           say('The specified account has not performed any migration', :red) |  | ||||||
|           exit(1) |  | ||||||
|         end |  | ||||||
| 
 | 
 | ||||||
|         unless options[:force] || migration.target_account_id == account.moved_to_account_id |         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 | ||||||
|           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 |  | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       if options[:target] |       if options[:target] | ||||||
|         target_account = ResolveAccountService.new.call(options[:target]) |         target_account = ResolveAccountService.new.call(options[:target]) | ||||||
| 
 | 
 | ||||||
|         if target_account.nil? |         fail_with_message "The specified target account could not be found: #{options[:target]}" if target_account.nil? | ||||||
|           say("The specified target account could not be found: #{options[:target]}", :red) |  | ||||||
|           exit(1) |  | ||||||
|         end |  | ||||||
| 
 | 
 | ||||||
|         unless options[:force] || account.moved_to_account_id.nil? || account.moved_to_account_id == target_account.id |         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 | ||||||
|           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 |  | ||||||
| 
 | 
 | ||||||
|         begin |         begin | ||||||
|           migration = account.migrations.create!(acct: target_account.acct) |           migration = account.migrations.create!(acct: target_account.acct) | ||||||
|         rescue ActiveRecord::RecordInvalid => e |         rescue ActiveRecord::RecordInvalid => e | ||||||
|           say("Error: #{e.message}", :red) |           fail_with_message "Error: #{e.message}" | ||||||
|           exit(1) |  | ||||||
|         end |         end | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|  | @ -648,18 +578,18 @@ module Mastodon::CLI | ||||||
|     private |     private | ||||||
| 
 | 
 | ||||||
|     def report_errors(errors) |     def report_errors(errors) | ||||||
|       errors.each do |error| |       message = errors.map do |error| | ||||||
|         say('Failure/Error: ', :red) |         <<~STRING | ||||||
|         say(error.attribute) |           Failure/Error: #{error.attribute} | ||||||
|         say("    #{error.type}", :red) |               #{error.type} | ||||||
|       end |         STRING | ||||||
|  |       end.join | ||||||
|  | 
 | ||||||
|  |       fail_with_message message | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     def rotate_keys_for_account(account, delay = 0) |     def rotate_keys_for_account(account, delay = 0) | ||||||
|       if account.nil? |       fail_with_message 'No such account' if account.nil? | ||||||
|         say('No such account', :red) |  | ||||||
|         exit(1) |  | ||||||
|       end |  | ||||||
| 
 | 
 | ||||||
|       old_key = account.private_key |       old_key = account.private_key | ||||||
|       new_key = OpenSSL::PKey::RSA.new(2048) |       new_key = OpenSSL::PKey::RSA.new(2048) | ||||||
|  |  | ||||||
|  | @ -18,6 +18,10 @@ module Mastodon | ||||||
| 
 | 
 | ||||||
|       private |       private | ||||||
| 
 | 
 | ||||||
|  |       def fail_with_message(message) | ||||||
|  |         raise Thor::Error, message | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|       def pastel |       def pastel | ||||||
|         @pastel ||= Pastel.new |         @pastel ||= Pastel.new | ||||||
|       end |       end | ||||||
|  |  | ||||||
|  | @ -31,8 +31,7 @@ module Mastodon::CLI | ||||||
|           recount_status_stats(status) |           recount_status_stats(status) | ||||||
|         end |         end | ||||||
|       else |       else | ||||||
|         say("Unknown type: #{type}", :red) |         fail_with_message "Unknown type: #{type}" | ||||||
|         exit(1) |  | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       say |       say | ||||||
|  |  | ||||||
|  | @ -41,11 +41,9 @@ module Mastodon::CLI | ||||||
| 
 | 
 | ||||||
|       # Sanity check on command arguments |       # Sanity check on command arguments | ||||||
|       if options[:limited_federation_mode] && !domains.empty? |       if options[:limited_federation_mode] && !domains.empty? | ||||||
|         say('DOMAIN parameter not supported with --limited-federation-mode', :red) |         fail_with_message 'DOMAIN parameter not supported with --limited-federation-mode' | ||||||
|         exit(1) |  | ||||||
|       elsif domains.empty? && !options[:limited_federation_mode] |       elsif domains.empty? && !options[:limited_federation_mode] | ||||||
|         say('No domain(s) given', :red) |         fail_with_message 'No domain(s) given' | ||||||
|         exit(1) |  | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       # Build scopes from command arguments |       # Build scopes from command arguments | ||||||
|  |  | ||||||
|  | @ -30,10 +30,7 @@ module Mastodon::CLI | ||||||
|       it at the root. |       it at the root. | ||||||
|     LONG_DESC |     LONG_DESC | ||||||
|     def add(*domains) |     def add(*domains) | ||||||
|       if domains.empty? |       fail_with_message 'No domain(s) given' if domains.empty? | ||||||
|         say('No domain(s) given', :red) |  | ||||||
|         exit(1) |  | ||||||
|       end |  | ||||||
| 
 | 
 | ||||||
|       skipped = 0 |       skipped = 0 | ||||||
|       processed = 0 |       processed = 0 | ||||||
|  | @ -76,10 +73,7 @@ module Mastodon::CLI | ||||||
| 
 | 
 | ||||||
|     desc 'remove DOMAIN...', 'Remove e-mail domain blocks' |     desc 'remove DOMAIN...', 'Remove e-mail domain blocks' | ||||||
|     def remove(*domains) |     def remove(*domains) | ||||||
|       if domains.empty? |       fail_with_message 'No domain(s) given' if domains.empty? | ||||||
|         say('No domain(s) given', :red) |  | ||||||
|         exit(1) |  | ||||||
|       end |  | ||||||
| 
 | 
 | ||||||
|       skipped = 0 |       skipped = 0 | ||||||
|       processed = 0 |       processed = 0 | ||||||
|  |  | ||||||
|  | @ -86,14 +86,8 @@ module Mastodon::CLI | ||||||
|       category         = CustomEmojiCategory.find_by(name: options[:category]) |       category         = CustomEmojiCategory.find_by(name: options[:category]) | ||||||
|       export_file_name = File.join(path, 'export.tar.gz') |       export_file_name = File.join(path, 'export.tar.gz') | ||||||
| 
 | 
 | ||||||
|       if File.file?(export_file_name) && !options[:overwrite] |       fail_with_message "Archive already exists! Use '--overwrite' to overwrite it!" if File.file?(export_file_name) && !options[:overwrite] | ||||||
|         say("Archive already exists! Use '--overwrite' to overwrite it!") |       fail_with_message "Unable to find category '#{options[:category]}'!" if category.nil? && options[:category] | ||||||
|         exit 1 |  | ||||||
|       end |  | ||||||
|       if category.nil? && options[:category] |  | ||||||
|         say("Unable to find category '#{options[:category]}'!") |  | ||||||
|         exit 1 |  | ||||||
|       end |  | ||||||
| 
 | 
 | ||||||
|       File.open(export_file_name, 'wb') do |file| |       File.open(export_file_name, 'wb') do |file| | ||||||
|         Zlib::GzipWriter.wrap(file) do |gzip| |         Zlib::GzipWriter.wrap(file) do |gzip| | ||||||
|  |  | ||||||
|  | @ -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) |             say('Every deletion notice has been sent! You can safely delete all data and decomission your servers!', :green) | ||||||
|           end |           end | ||||||
| 
 | 
 | ||||||
|           exit(0) |           raise(SystemExit) | ||||||
|         end |         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) |         say(<<~WARNING, :yellow) | ||||||
|           This operation WILL NOT be reversible. |           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). |           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 |         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) |         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: |           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} |             SELF_DESTRUCT=#{self_destruct_value} | ||||||
|           You can re-run this command to see the state of the self-destruct process. |           You can re-run this command to see the state of the self-destruct process. | ||||||
|         INSTRUCTIONS |         INSTRUCTIONS | ||||||
|       rescue Interrupt |  | ||||||
|         exit(1) |  | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       private |       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 |       def self_destruct_value | ||||||
|         Rails |         Rails | ||||||
|           .application |           .application | ||||||
|  |  | ||||||
|  | @ -27,17 +27,13 @@ module Mastodon::CLI | ||||||
|       elsif username.present? |       elsif username.present? | ||||||
|         account = Account.find_local(username) |         account = Account.find_local(username) | ||||||
| 
 | 
 | ||||||
|         if account.nil? |         fail_with_message 'No such account' if account.nil? | ||||||
|           say('No such account', :red) |  | ||||||
|           exit(1) |  | ||||||
|         end |  | ||||||
| 
 | 
 | ||||||
|         PrecomputeFeedService.new.call(account) unless dry_run? |         PrecomputeFeedService.new.call(account) unless dry_run? | ||||||
| 
 | 
 | ||||||
|         say("OK #{dry_run_mode_suffix}", :green, true) |         say("OK #{dry_run_mode_suffix}", :green, true) | ||||||
|       else |       else | ||||||
|         say('No account(s) given', :red) |         fail_with_message 'No account(s) given' | ||||||
|         exit(1) |  | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -20,10 +20,7 @@ module Mastodon::CLI | ||||||
|       option to overwrite it. |       option to overwrite it. | ||||||
|     LONG_DESC |     LONG_DESC | ||||||
|     def add(*addresses) |     def add(*addresses) | ||||||
|       if addresses.empty? |       fail_with_message 'No IP(s) given' if addresses.empty? | ||||||
|         say('No IP(s) given', :red) |  | ||||||
|         exit(1) |  | ||||||
|       end |  | ||||||
| 
 | 
 | ||||||
|       skipped   = 0 |       skipped   = 0 | ||||||
|       processed = 0 |       processed = 0 | ||||||
|  | @ -70,10 +67,7 @@ module Mastodon::CLI | ||||||
|       cover the given IP(s). |       cover the given IP(s). | ||||||
|     LONG_DESC |     LONG_DESC | ||||||
|     def remove(*addresses) |     def remove(*addresses) | ||||||
|       if addresses.empty? |       fail_with_message 'No IP(s) given' if addresses.empty? | ||||||
|         say('No IP(s) given', :red) |  | ||||||
|         exit(1) |  | ||||||
|       end |  | ||||||
| 
 | 
 | ||||||
|       processed = 0 |       processed = 0 | ||||||
|       skipped   = 0 |       skipped   = 0 | ||||||
|  |  | ||||||
|  | @ -199,26 +199,24 @@ module Mastodon::CLI | ||||||
| 
 | 
 | ||||||
|     def verify_schema_version! |     def verify_schema_version! | ||||||
|       if migrator_version < MIN_SUPPORTED_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 |         fail_with_message <<~ERROR | ||||||
|         say 'Please update to at least Mastodon 3.0.0 before running this script.', :red |           Your version of the database schema is too old and is not supported by this script. | ||||||
|         exit(1) |           Please update to at least Mastodon 3.0.0 before running this script. | ||||||
|  |         ERROR | ||||||
|       elsif migrator_version > MAX_SUPPORTED_VERSION |       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 |         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 | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     def verify_sidekiq_not_active! |     def verify_sidekiq_not_active! | ||||||
|       if Sidekiq::ProcessSet.new.any? |       fail_with_message 'It seems Sidekiq is running. All Mastodon processes need to be stopped when using this script.' 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 |  | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     def verify_backup_warning! |     def verify_backup_warning! | ||||||
|       say 'This task will take a long time to run and is potentially destructive.', :yellow |       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 |       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 |     end | ||||||
| 
 | 
 | ||||||
|     def deduplicate_accounts! |     def deduplicate_accounts! | ||||||
|  |  | ||||||
|  | @ -31,15 +31,9 @@ module Mastodon::CLI | ||||||
|       following anyone locally are pruned. |       following anyone locally are pruned. | ||||||
|     DESC |     DESC | ||||||
|     def remove |     def remove | ||||||
|       if options[:prune_profiles] && options[:remove_headers] |       fail_with_message '--prune-profiles and --remove-headers should not be specified simultaneously' if options[:prune_profiles] && options[:remove_headers] | ||||||
|         say('--prune-profiles and --remove-headers should not be specified simultaneously', :red, true) |  | ||||||
|         exit(1) |  | ||||||
|       end |  | ||||||
| 
 | 
 | ||||||
|       if options[:include_follows] && !(options[:prune_profiles] || options[:remove_headers]) |       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]) | ||||||
|         say('--include-follows can only be used with --prune-profiles or --remove-headers', :red, true) |  | ||||||
|         exit(1) |  | ||||||
|       end |  | ||||||
|       time_ago = options[:days].days.ago |       time_ago = options[:days].days.ago | ||||||
| 
 | 
 | ||||||
|       if options[:prune_profiles] || options[:remove_headers] |       if options[:prune_profiles] || options[:remove_headers] | ||||||
|  | @ -156,11 +150,9 @@ module Mastodon::CLI | ||||||
|           end |           end | ||||||
|         end |         end | ||||||
|       when :fog |       when :fog | ||||||
|         say('The fog storage driver is not supported for this operation at this time', :red) |         fail_with_message 'The fog storage driver is not supported for this operation at this time' | ||||||
|         exit(1) |  | ||||||
|       when :azure |       when :azure | ||||||
|         say('The azure storage driver is not supported for this operation at this time', :red) |         fail_with_message 'The azure storage driver is not supported for this operation at this time' | ||||||
|         exit(1) |  | ||||||
|       when :filesystem |       when :filesystem | ||||||
|         require 'find' |         require 'find' | ||||||
| 
 | 
 | ||||||
|  | @ -254,10 +246,7 @@ module Mastodon::CLI | ||||||
|         username, domain = options[:account].split('@') |         username, domain = options[:account].split('@') | ||||||
|         account = Account.find_remote(username, domain) |         account = Account.find_remote(username, domain) | ||||||
| 
 | 
 | ||||||
|         if account.nil? |         fail_with_message 'No such account' if account.nil? | ||||||
|           say('No such account', :red) |  | ||||||
|           exit(1) |  | ||||||
|         end |  | ||||||
| 
 | 
 | ||||||
|         scope = MediaAttachment.where(account_id: account.id) |         scope = MediaAttachment.where(account_id: account.id) | ||||||
|       elsif options[:domain] |       elsif options[:domain] | ||||||
|  | @ -265,8 +254,7 @@ module Mastodon::CLI | ||||||
|       elsif options[:days].present? |       elsif options[:days].present? | ||||||
|         scope = MediaAttachment.remote |         scope = MediaAttachment.remote | ||||||
|       else |       else | ||||||
|         say('Specify the source of media attachments', :red) |         fail_with_message 'Specify the source of media attachments' | ||||||
|         exit(1) |  | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       scope = scope.where('media_attachments.id > ?', Mastodon::Snowflake.id_at(options[:days].days.ago, with_random: false)) if options[:days].present? |       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 = path.split('/')[2..] | ||||||
|       path_segments.delete('cache') |       path_segments.delete('cache') | ||||||
| 
 | 
 | ||||||
|       unless VALID_PATH_SEGMENTS_SIZE.include?(path_segments.size) |       fail_with_message 'Not a media URL' unless VALID_PATH_SEGMENTS_SIZE.include?(path_segments.size) | ||||||
|         say('Not a media URL', :red) |  | ||||||
|         exit(1) |  | ||||||
|       end |  | ||||||
| 
 | 
 | ||||||
|       model_name = path_segments.first.classify |       model_name = path_segments.first.classify | ||||||
|       record_id  = path_segments[2..-2].join.to_i |       record_id  = path_segments[2..-2].join.to_i | ||||||
| 
 | 
 | ||||||
|       unless PRELOAD_MODEL_WHITELIST.include?(model_name) |       fail_with_message "Cannot find corresponding model: #{model_name}" unless PRELOAD_MODEL_WHITELIST.include?(model_name) | ||||||
|         say("Cannot find corresponding model: #{model_name}", :red) |  | ||||||
|         exit(1) |  | ||||||
|       end |  | ||||||
| 
 | 
 | ||||||
|       record = model_name.constantize.find_by(id: record_id) |       record = model_name.constantize.find_by(id: record_id) | ||||||
|       record = record.status if record.respond_to?(:status) |       record = record.status if record.respond_to?(:status) | ||||||
| 
 | 
 | ||||||
|       unless record |       fail_with_message 'Cannot find corresponding record' unless record | ||||||
|         say('Cannot find corresponding record', :red) |  | ||||||
|         exit(1) |  | ||||||
|       end |  | ||||||
| 
 | 
 | ||||||
|       display_url = ActivityPub::TagManager.instance.url_for(record) |       display_url = ActivityPub::TagManager.instance.url_for(record) | ||||||
| 
 | 
 | ||||||
|       if display_url.blank? |       fail_with_message 'No public URL for this type of record' if display_url.blank? | ||||||
|         say('No public URL for this type of record', :red) |  | ||||||
|         exit(1) |  | ||||||
|       end |  | ||||||
| 
 | 
 | ||||||
|       say(display_url, :blue) |       say(display_url, :blue) | ||||||
|     rescue Addressable::URI::InvalidURIError |     rescue Addressable::URI::InvalidURIError | ||||||
|       say('Invalid URL', :red) |       fail_with_message 'Invalid URL' | ||||||
|       exit(1) |  | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     private |     private | ||||||
|  |  | ||||||
|  | @ -25,10 +25,7 @@ module Mastodon::CLI | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     def parallelize_with_progress(scope) |     def parallelize_with_progress(scope) | ||||||
|       if options[:concurrency] < 1 |       fail_with_message 'Cannot run with this concurrency setting, must be at least 1' if options[:concurrency] < 1 | ||||||
|         say('Cannot run with this concurrency setting, must be at least 1', :red) |  | ||||||
|         exit(1) |  | ||||||
|       end |  | ||||||
| 
 | 
 | ||||||
|       reset_connection_pools! |       reset_connection_pools! | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -110,17 +110,11 @@ module Mastodon::CLI | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     def verify_deploy_concurrency! |     def verify_deploy_concurrency! | ||||||
|       return unless options[:concurrency] < 1 |       fail_with_message 'Cannot run with this concurrency setting, must be at least 1' if options[:concurrency] < 1 | ||||||
| 
 |  | ||||||
|       say('Cannot run with this concurrency setting, must be at least 1', :red) |  | ||||||
|       exit(1) |  | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     def verify_deploy_batch_size! |     def verify_deploy_batch_size! | ||||||
|       return unless options[:batch_size] < 1 |       fail_with_message 'Cannot run with this batch_size setting, must be at least 1' if options[:batch_size] < 1 | ||||||
| 
 |  | ||||||
|       say('Cannot run with this batch_size setting, must be at least 1', :red) |  | ||||||
|       exit(1) |  | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     def progress_output_options |     def progress_output_options | ||||||
|  |  | ||||||
|  | @ -26,10 +26,7 @@ module Mastodon::CLI | ||||||
|       indices before commencing, and removes them afterward. |       indices before commencing, and removes them afterward. | ||||||
|     LONG_DESC |     LONG_DESC | ||||||
|     def remove |     def remove | ||||||
|       if options[:batch_size] < 1 |       fail_with_message 'Cannot run with this batch_size setting, must be at least 1' if options[:batch_size] < 1 | ||||||
|         say('Cannot run with this batch_size setting, must be at least 1', :red) |  | ||||||
|         exit(1) |  | ||||||
|       end |  | ||||||
| 
 | 
 | ||||||
|       remove_statuses |       remove_statuses | ||||||
|       vacuum_and_analyze_statuses |       vacuum_and_analyze_statuses | ||||||
|  |  | ||||||
|  | @ -103,13 +103,11 @@ module Mastodon::CLI | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     def upgrade_storage_fog(_progress, _attachment, _style) |     def upgrade_storage_fog(_progress, _attachment, _style) | ||||||
|       say('The fog storage driver is not supported for this operation at this time', :red) |       fail_with_message 'The fog storage driver is not supported for this operation at this time' | ||||||
|       exit(1) |  | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     def upgrade_storage_azure(_progress, _attachment, _style) |     def upgrade_storage_azure(_progress, _attachment, _style) | ||||||
|       say('The azure storage driver is not supported for this operation at this time', :red) |       fail_with_message 'The azure storage driver is not supported for this operation at this time' | ||||||
|       exit(1) |  | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     def upgrade_storage_filesystem(progress, attachment, style) |     def upgrade_storage_filesystem(progress, attachment, style) | ||||||
|  |  | ||||||
|  | @ -65,8 +65,7 @@ describe Mastodon::CLI::Accounts do | ||||||
| 
 | 
 | ||||||
|           it 'exits with an error message' do |           it 'exits with an error message' do | ||||||
|             expect { subject } |             expect { subject } | ||||||
|               .to output_results('Failure/Error: email') |               .to raise_error(Thor::Error, %r{Failure/Error: email}) | ||||||
|               .and raise_error(SystemExit) |  | ||||||
|           end |           end | ||||||
|         end |         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 |           it 'exits with an error message indicating the role name was not found' do | ||||||
|             expect { subject } |             expect { subject } | ||||||
|               .to output_results('Cannot find user role with that name') |               .to raise_error(Thor::Error, 'Cannot find user role with that name') | ||||||
|               .and raise_error(SystemExit) |  | ||||||
|           end |           end | ||||||
|         end |         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 |       it 'exits with an error message indicating the user was not found' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results('No user with such username') |           .to raise_error(Thor::Error, 'No user with such username') | ||||||
|           .and raise_error(SystemExit) |  | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  | @ -214,8 +211,7 @@ describe Mastodon::CLI::Accounts do | ||||||
| 
 | 
 | ||||||
|           it 'exits with an error message indicating the role was not found' do |           it 'exits with an error message indicating the role was not found' do | ||||||
|             expect { subject } |             expect { subject } | ||||||
|               .to output_results('Cannot find user role with that name') |               .to raise_error(Thor::Error, 'Cannot find user role with that name') | ||||||
|               .and raise_error(SystemExit) |  | ||||||
|           end |           end | ||||||
|         end |         end | ||||||
| 
 | 
 | ||||||
|  | @ -364,8 +360,7 @@ describe Mastodon::CLI::Accounts do | ||||||
| 
 | 
 | ||||||
|         it 'exits with an error message' do |         it 'exits with an error message' do | ||||||
|           expect { subject } |           expect { subject } | ||||||
|             .to output_results('Failure/Error: email') |             .to raise_error(Thor::Error, %r{Failure/Error: email}) | ||||||
|             .and raise_error(SystemExit) |  | ||||||
|         end |         end | ||||||
|       end |       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 |       it 'exits with an error message indicating that only one should be used' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results('Use username or --email, not both') |           .to raise_error(Thor::Error, 'Use username or --email, not both') | ||||||
|           .and raise_error(SystemExit) |  | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     context 'when neither username nor --email are provided' do |     context 'when neither username nor --email are provided' do | ||||||
|       it 'exits with an error message indicating that no username was provided' do |       it 'exits with an error message indicating that no username was provided' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results('No username provided') |           .to raise_error(Thor::Error, 'No username provided') | ||||||
|           .and raise_error(SystemExit) |  | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  | @ -425,8 +418,7 @@ describe Mastodon::CLI::Accounts do | ||||||
| 
 | 
 | ||||||
|         it 'exits with an error message indicating that no user was found' do |         it 'exits with an error message indicating that no user was found' do | ||||||
|           expect { subject } |           expect { subject } | ||||||
|             .to output_results('No user with such username') |             .to raise_error(Thor::Error, 'No user with such username') | ||||||
|             .and raise_error(SystemExit) |  | ||||||
|         end |         end | ||||||
|       end |       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 |         it 'exits with an error message indicating that no user was found' do | ||||||
|           expect { subject } |           expect { subject } | ||||||
|             .to output_results('No user with such email') |             .to raise_error(Thor::Error, 'No user with such email') | ||||||
|             .and raise_error(SystemExit) |  | ||||||
|         end |         end | ||||||
|       end |       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 |         it 'exits with an error message indicating that the number must be positive' do | ||||||
|           expect { subject } |           expect { subject } | ||||||
|             .to output_results('Number must be positive') |             .to raise_error(Thor::Error, 'Number must be positive') | ||||||
|             .and raise_error(SystemExit) |  | ||||||
|         end |         end | ||||||
|       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 |         it 'exits with an error message indicating that no such account was found' do | ||||||
|           expect { subject } |           expect { subject } | ||||||
|             .to output_results('No such account') |             .to raise_error(Thor::Error, 'No such account') | ||||||
|             .and raise_error(SystemExit) |  | ||||||
|         end |         end | ||||||
|       end |       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 |       it 'exits with an error message indicating that no account with the given username was found' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results('No such account') |           .to raise_error(Thor::Error, 'No such account') | ||||||
|           .and raise_error(SystemExit) |  | ||||||
|       end |       end | ||||||
|     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 |       it 'exits with an error message indicating that no account with the given username was found' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results('No such account') |           .to raise_error(Thor::Error, 'No such account') | ||||||
|           .and raise_error(SystemExit) |  | ||||||
|       end |       end | ||||||
|     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 |       it 'exits with an error message indicating that there is no such account' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results('No user with such username') |           .to raise_error(Thor::Error, 'No user with such username') | ||||||
|           .and raise_error(SystemExit) |  | ||||||
|       end |       end | ||||||
|     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) |           allow(Account).to receive(:find_remote).with(account_example_com_b.username, account_example_com_b.domain).and_return(nil) | ||||||
| 
 | 
 | ||||||
|           expect { subject } |           expect { subject } | ||||||
|             .to output_results('No such account') |             .to raise_error(Thor::Error, 'No such account') | ||||||
|             .and raise_error(SystemExit) |  | ||||||
|         end |         end | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|  | @ -892,8 +877,7 @@ describe Mastodon::CLI::Accounts do | ||||||
|     context 'when neither a list of accts nor options are provided' do |     context 'when neither a list of accts nor options are provided' do | ||||||
|       it 'exits with an error message' do |       it 'exits with an error message' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results('No account(s) given') |           .to raise_error(Thor::Error, 'No account(s) given') | ||||||
|           .and raise_error(SystemExit) |  | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  | @ -904,8 +888,7 @@ describe Mastodon::CLI::Accounts do | ||||||
|     context 'when neither username nor --all option are given' do |     context 'when neither username nor --all option are given' do | ||||||
|       it 'exits with an error message' do |       it 'exits with an error message' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results('No account(s) given') |           .to raise_error(Thor::Error, 'No account(s) given') | ||||||
|           .and raise_error(SystemExit) |  | ||||||
|       end |       end | ||||||
|     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 |       it 'exits with an error message when the specified username is not found' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results('No such account') |           .to raise_error(Thor::Error, 'No such account') | ||||||
|           .and raise_error(SystemExit) |  | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  | @ -980,8 +962,7 @@ describe Mastodon::CLI::Accounts do | ||||||
|     shared_examples 'an account not found' do |acct| |     shared_examples 'an account not found' do |acct| | ||||||
|       it 'exits with an error message indicating that there is no such account' do |       it 'exits with an error message indicating that there is no such account' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results("No such account (#{acct})") |           .to raise_error(Thor::Error, "No such account (#{acct})") | ||||||
|           .and raise_error(SystemExit) |  | ||||||
|       end |       end | ||||||
|     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 |       it 'exits with an error message indicating that the accounts do not have the same pub key' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results("Accounts don't have the same public key, might not be duplicates!\nOverride with --force") |           .to raise_error(Thor::Error, "Accounts don't have the same public key, might not be duplicates!\nOverride with --force\n") | ||||||
|           .and raise_error(SystemExit) |  | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       context 'with --force option' do |       context 'with --force option' do | ||||||
|  | @ -1200,8 +1180,7 @@ describe Mastodon::CLI::Accounts do | ||||||
|     context 'when no option is given' do |     context 'when no option is given' do | ||||||
|       it 'exits with an error message indicating that at least one option is required' do |       it 'exits with an error message indicating that at least one option is required' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results('Please specify either --follows or --followers, or both') |           .to raise_error(Thor::Error, 'Please specify either --follows or --followers, or both') | ||||||
|           .and raise_error(SystemExit) |  | ||||||
|       end |       end | ||||||
|     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 |       it 'exits with an error message indicating that there is no such account' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results('No such account') |           .to raise_error(Thor::Error, 'No such account') | ||||||
|           .and raise_error(SystemExit) |  | ||||||
|       end |       end | ||||||
|     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 |       it 'exits with an error message indicating that using both options is not possible' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results('Use --replay or --target, not both') |           .to raise_error(Thor::Error, 'Use --replay or --target, not both') | ||||||
|           .and raise_error(SystemExit) |  | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     context 'when no option is given' do |     context 'when no option is given' do | ||||||
|       it 'exits with an error message indicating that at least one option must be used' do |       it 'exits with an error message indicating that at least one option must be used' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results('Use either --replay or --target') |           .to raise_error(Thor::Error, 'Use either --replay or --target') | ||||||
|           .and raise_error(SystemExit) |  | ||||||
|       end |       end | ||||||
|     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 |       it 'exits with an error message indicating that there is no such account' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results("No such account: #{arguments.first}") |           .to raise_error(Thor::Error, "No such account: #{arguments.first}") | ||||||
|           .and raise_error(SystemExit) |  | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  | @ -1398,8 +1373,7 @@ describe Mastodon::CLI::Accounts do | ||||||
|       context 'when the specified account has no previous migrations' 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 |         it 'exits with an error message indicating that the given account has no previous migrations' do | ||||||
|           expect { subject } |           expect { subject } | ||||||
|             .to output_results('The specified account has not performed any migration') |             .to raise_error(Thor::Error, 'The specified account has not performed any migration') | ||||||
|             .and raise_error(SystemExit) |  | ||||||
|         end |         end | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|  | @ -1421,8 +1395,7 @@ describe Mastodon::CLI::Accounts do | ||||||
| 
 | 
 | ||||||
|           it 'exits with an error message' do |           it 'exits with an error message' do | ||||||
|             expect { subject } |             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') |               .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') | ||||||
|               .and raise_error(SystemExit) |  | ||||||
|           end |           end | ||||||
|         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 |         it 'exits with an error message indicating that there is no such account' do | ||||||
|           expect { subject } |           expect { subject } | ||||||
|             .to output_results("The specified target account could not be found: #{options[:target]}") |             .to raise_error(Thor::Error, "The specified target account could not be found: #{options[:target]}") | ||||||
|             .and raise_error(SystemExit) |  | ||||||
|         end |         end | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|  | @ -1474,8 +1446,7 @@ describe Mastodon::CLI::Accounts do | ||||||
|       context 'when the migration record is invalid' do |       context 'when the migration record is invalid' do | ||||||
|         it 'exits with an error indicating that the validation failed' do |         it 'exits with an error indicating that the validation failed' do | ||||||
|           expect { subject } |           expect { subject } | ||||||
|             .to output_results('Error: Validation failed') |             .to raise_error(Thor::Error, /Error: Validation failed/) | ||||||
|             .and raise_error(SystemExit) |  | ||||||
|         end |         end | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|  | @ -1486,8 +1457,7 @@ describe Mastodon::CLI::Accounts do | ||||||
| 
 | 
 | ||||||
|         it 'exits with an error message' do |         it 'exits with an error message' do | ||||||
|           expect { subject } |           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') |             .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') | ||||||
|             .and raise_error(SystemExit) |  | ||||||
|         end |         end | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -64,8 +64,7 @@ describe Mastodon::CLI::Cache do | ||||||
| 
 | 
 | ||||||
|       it 'Exits with an error message' do |       it 'Exits with an error message' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results('Unknown') |           .to raise_error(Thor::Error, /Unknown/) | ||||||
|           .and raise_error(SystemExit) |  | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  | @ -35,8 +35,7 @@ describe Mastodon::CLI::EmailDomainBlocks do | ||||||
|     context 'without any options' do |     context 'without any options' do | ||||||
|       it 'warns about usage and exits' do |       it 'warns about usage and exits' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results('No domain(s) given') |           .to raise_error(Thor::Error, 'No domain(s) given') | ||||||
|           .and raise_error(SystemExit) |  | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  | @ -72,8 +71,7 @@ describe Mastodon::CLI::EmailDomainBlocks do | ||||||
|     context 'without any options' do |     context 'without any options' do | ||||||
|       it 'warns about usage and exits' do |       it 'warns about usage and exits' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results('No domain(s) given') |           .to raise_error(Thor::Error, 'No domain(s) given') | ||||||
|           .and raise_error(SystemExit) |  | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -42,8 +42,7 @@ describe Mastodon::CLI::Feeds do | ||||||
| 
 | 
 | ||||||
|       it 'displays an error and exits' do |       it 'displays an error and exits' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results('No such account') |           .to raise_error(Thor::Error, 'No such account') | ||||||
|           .and raise_error(SystemExit) |  | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  | @ -144,8 +144,7 @@ describe Mastodon::CLI::IpBlocks do | ||||||
| 
 | 
 | ||||||
|       it 'exits with an error message' do |       it 'exits with an error message' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results('No IP(s) given') |           .to raise_error(Thor::Error, 'No IP(s) given') | ||||||
|           .and raise_error(SystemExit) |  | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  | @ -235,8 +234,7 @@ describe Mastodon::CLI::IpBlocks do | ||||||
|     context 'when no IP address is provided' do |     context 'when no IP address is provided' do | ||||||
|       it 'exits with an error message' do |       it 'exits with an error message' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results('No IP(s) given') |           .to raise_error(Thor::Error, 'No IP(s) given') | ||||||
|           .and raise_error(SystemExit) |  | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  | @ -104,9 +104,9 @@ describe Mastodon::CLI::Main do | ||||||
|           answer_hostname_incorrectly |           answer_hostname_incorrectly | ||||||
|         end |         end | ||||||
| 
 | 
 | ||||||
|         it 'exits silently' do |         it 'exits with mismatch error message' do | ||||||
|           expect { subject } |           expect { subject } | ||||||
|             .to raise_error(SystemExit) |             .to raise_error(Thor::Error, /Domains do not match/) | ||||||
|         end |         end | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|  | @ -119,7 +119,7 @@ describe Mastodon::CLI::Main do | ||||||
|         it 'passes first step but stops before instructions' do |         it 'passes first step but stops before instructions' do | ||||||
|           expect { subject } |           expect { subject } | ||||||
|             .to output_results('operation WILL NOT') |             .to output_results('operation WILL NOT') | ||||||
|             .and raise_error(SystemExit) |             .and raise_error(Thor::Error, /Self-destruct will not begin/) | ||||||
|         end |         end | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -22,8 +22,7 @@ describe Mastodon::CLI::Maintenance do | ||||||
| 
 | 
 | ||||||
|       it 'Exits with error message' do |       it 'Exits with error message' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results('is too old') |           .to raise_error(Thor::Error, /is too old/) | ||||||
|           .and raise_error(SystemExit) |  | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  | @ -36,7 +35,7 @@ describe Mastodon::CLI::Maintenance do | ||||||
|       it 'Exits with error message' do |       it 'Exits with error message' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results('more recent') |           .to output_results('more recent') | ||||||
|           .and raise_error(SystemExit) |           .and raise_error(Thor::Error, /more recent/) | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  | @ -48,8 +47,7 @@ describe Mastodon::CLI::Maintenance do | ||||||
| 
 | 
 | ||||||
|       it 'Exits with error message' do |       it 'Exits with error message' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results('Sidekiq is running') |           .to raise_error(Thor::Error, /Sidekiq is running/) | ||||||
|           .and raise_error(SystemExit) |  | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -20,8 +20,7 @@ describe Mastodon::CLI::Media do | ||||||
| 
 | 
 | ||||||
|       it 'warns about usage and exits' do |       it 'warns about usage and exits' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results('--prune-profiles and --remove-headers should not be specified simultaneously') |           .to raise_error(Thor::Error, '--prune-profiles and --remove-headers should not be specified simultaneously') | ||||||
|           .and raise_error(SystemExit) |  | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  | @ -30,8 +29,7 @@ describe Mastodon::CLI::Media do | ||||||
| 
 | 
 | ||||||
|       it 'warns about usage and exits' do |       it 'warns about usage and exits' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results('--include-follows can only be used with --prune-profiles or --remove-headers') |           .to raise_error(Thor::Error, '--include-follows can only be used with --prune-profiles or --remove-headers') | ||||||
|           .and raise_error(SystemExit) |  | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  | @ -98,8 +96,7 @@ describe Mastodon::CLI::Media do | ||||||
| 
 | 
 | ||||||
|       it 'warns about url and exits' do |       it 'warns about url and exits' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results('Not a media URL') |           .to raise_error(Thor::Error, 'Not a media URL') | ||||||
|           .and raise_error(SystemExit) |  | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  | @ -121,8 +118,7 @@ describe Mastodon::CLI::Media do | ||||||
|     context 'without any options' do |     context 'without any options' do | ||||||
|       it 'warns about usage and exits' do |       it 'warns about usage and exits' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results('Specify the source') |           .to raise_error(Thor::Error, /Specify the source/) | ||||||
|           .and raise_error(SystemExit) |  | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  | @ -147,8 +143,7 @@ describe Mastodon::CLI::Media do | ||||||
| 
 | 
 | ||||||
|         it 'warns about usage and exits' do |         it 'warns about usage and exits' do | ||||||
|           expect { subject } |           expect { subject } | ||||||
|             .to output_results('No such account') |             .to raise_error(Thor::Error, 'No such account') | ||||||
|             .and raise_error(SystemExit) |  | ||||||
|         end |         end | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|  | @ -221,8 +216,7 @@ describe Mastodon::CLI::Media do | ||||||
| 
 | 
 | ||||||
|       it 'warns about usage and exits' do |       it 'warns about usage and exits' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results('azure storage driver is not supported') |           .to raise_error(Thor::Error, /azure storage driver is not supported/) | ||||||
|           .and raise_error(SystemExit) |  | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  | @ -233,8 +227,7 @@ describe Mastodon::CLI::Media do | ||||||
| 
 | 
 | ||||||
|       it 'warns about usage and exits' do |       it 'warns about usage and exits' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results('fog storage driver is not supported') |           .to raise_error(Thor::Error, /fog storage driver is not supported/) | ||||||
|           .and raise_error(SystemExit) |  | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -20,8 +20,7 @@ describe Mastodon::CLI::Search do | ||||||
| 
 | 
 | ||||||
|       it 'Exits with error message' do |       it 'Exits with error message' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results('this concurrency setting') |           .to raise_error(Thor::Error, /this concurrency setting/) | ||||||
|           .and raise_error(SystemExit) |  | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  | @ -30,8 +29,7 @@ describe Mastodon::CLI::Search do | ||||||
| 
 | 
 | ||||||
|       it 'Exits with error message' do |       it 'Exits with error message' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results('this batch_size setting') |           .to raise_error(Thor::Error, /this batch_size setting/) | ||||||
|           .and raise_error(SystemExit) |  | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -20,8 +20,7 @@ describe Mastodon::CLI::Statuses do | ||||||
| 
 | 
 | ||||||
|       it 'exits with error message' do |       it 'exits with error message' do | ||||||
|         expect { subject } |         expect { subject } | ||||||
|           .to output_results('Cannot run') |           .to raise_error(Thor::Error, /Cannot run/) | ||||||
|           .and raise_error(SystemExit) |  | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue