123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- # frozen_string_literal: true
- require_relative '../../config/boot'
- require_relative '../../config/environment'
- require_relative 'cli_helper'
- module Mastodon
- class UpgradeCLI < Thor
- include CLIHelper
- def self.exit_on_failure?
- true
- end
- CURRENT_STORAGE_SCHEMA_VERSION = 1
- option :dry_run, type: :boolean, default: false
- option :verbose, type: :boolean, default: false, aliases: [:v]
- desc 'storage-schema', 'Upgrade storage schema of various file attachments to the latest version'
- long_desc <<~LONG_DESC
- Iterates over every file attachment of every record and, if its storage schema is outdated, performs the
- necessary upgrade to the latest one. In practice this means e.g. moving files to different directories.
- Will most likely take a long time.
- LONG_DESC
- def storage_schema
- progress = create_progress_bar(nil)
- dry_run = dry_run? ? ' (DRY RUN)' : ''
- records = 0
- klasses = [
- Account,
- CustomEmoji,
- MediaAttachment,
- PreviewCard,
- ]
- klasses.each do |klass|
- attachment_names = klass.attachment_definitions.keys
- klass.find_each do |record|
- attachment_names.each do |attachment_name|
- attachment = record.public_send(attachment_name)
- upgraded = false
- next if attachment.blank? || attachment.storage_schema_version >= CURRENT_STORAGE_SCHEMA_VERSION
- styles = attachment.styles.keys
- styles << :original unless styles.include?(:original)
- styles.each do |style|
- success = begin
- case Paperclip::Attachment.default_options[:storage]
- when :s3
- upgrade_storage_s3(progress, attachment, style)
- when :fog
- upgrade_storage_fog(progress, attachment, style)
- when :filesystem
- upgrade_storage_filesystem(progress, attachment, style)
- end
- end
- upgraded = true if style == :original && success
- progress.increment
- end
- attachment.instance_write(:storage_schema_version, CURRENT_STORAGE_SCHEMA_VERSION) if upgraded
- end
- if record.changed?
- record.save unless dry_run?
- records += 1
- end
- end
- end
- progress.total = progress.progress
- progress.finish
- say("Upgraded storage schema of #{records} records#{dry_run}", :green, true)
- end
- private
- def upgrade_storage_s3(progress, attachment, style)
- previous_storage_schema_version = attachment.storage_schema_version
- object = attachment.s3_object(style)
- success = true
- attachment.instance_write(:storage_schema_version, CURRENT_STORAGE_SCHEMA_VERSION)
- new_object = attachment.s3_object(style)
- if new_object.key != object.key && object.exists?
- progress.log("Moving #{object.key} to #{new_object.key}") if options[:verbose]
- begin
- object.move_to(new_object, acl: attachment.s3_permissions(style)) unless dry_run?
- rescue => e
- progress.log(pastel.red("Error processing #{object.key}: #{e}"))
- success = false
- end
- end
- # Because we move files style-by-style, it's important to restore
- # previous version at the end. The upgrade will be recorded after
- # all styles are updated
- attachment.instance_write(:storage_schema_version, previous_storage_schema_version)
- success
- 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)
- end
- def upgrade_storage_filesystem(progress, attachment, style)
- previous_storage_schema_version = attachment.storage_schema_version
- previous_path = attachment.path(style)
- success = true
- attachment.instance_write(:storage_schema_version, CURRENT_STORAGE_SCHEMA_VERSION)
- upgraded_path = attachment.path(style)
- if upgraded_path != previous_path && File.exist?(previous_path)
- progress.log("Moving #{previous_path} to #{upgraded_path}") if options[:verbose]
- begin
- unless dry_run?
- FileUtils.mkdir_p(File.dirname(upgraded_path))
- FileUtils.mv(previous_path, upgraded_path)
- begin
- FileUtils.rmdir(File.dirname(previous_path), parents: true)
- rescue Errno::ENOTEMPTY
- # OK
- end
- end
- rescue => e
- progress.log(pastel.red("Error processing #{previous_path}: #{e}"))
- success = false
- unless dry_run?
- begin
- FileUtils.rmdir(File.dirname(upgraded_path), parents: true)
- rescue Errno::ENOTEMPTY
- # OK
- end
- end
- end
- end
- # Because we move files style-by-style, it's important to restore
- # previous version at the end. The upgrade will be recorded after
- # all styles are updated
- attachment.instance_write(:storage_schema_version, previous_storage_schema_version)
- success
- end
- end
- end
|