- # 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
|