def upload(options)
      return if options[:skip_metadata]
      # it is not possible to create new languages, because
      # :keywords is not write-able on published versions
      # therefore skip it.
      verify_available_languages!(options) unless options[:edit_live]

      app = options[:app]

      details = app.details
      if options[:edit_live]
        # not all values are editable when using live_version
        v = app.live_version(platform: options[:platform])
        localised_options = LOCALISED_LIVE_VALUES
        non_localised_options = NON_LOCALISED_LIVE_VALUES

        if v.nil?
          UI.message("Couldn't find live version, editing the current version on App Store Connect instead")
          v = app.edit_version(platform: options[:platform])
          # we don't want to update the localised_options and non_localised_options
          # as we also check for `options[:edit_live]` at other areas in the code
          # by not touching those 2 variables, deliver is more consistent with what the option says
          # in the documentation
        end
      else
        v = app.edit_version(platform: options[:platform])
        localised_options = (LOCALISED_VERSION_VALUES + LOCALISED_APP_VALUES)
        non_localised_options = (NON_LOCALISED_VERSION_VALUES + NON_LOCALISED_APP_VALUES)
      end

      individual = options[:individual_metadata_items] || []
      localised_options.each do |key|
        current = options[key]
        next unless current

        unless current.kind_of?(Hash)
          UI.error("Error with provided '#{key}'. Must be a hash, the key being the language.")
          next
        end

        current.each do |language, value|
          next unless value.to_s.length > 0
          strip_value = value.to_s.strip
          if individual.include?(key.to_s)
            upload_individual_item(app, v, language, key, strip_value)
          else
            v.send(key)[language] = strip_value if LOCALISED_VERSION_VALUES.include?(key)
            details.send(key)[language] = strip_value if LOCALISED_APP_VALUES.include?(key)
          end
        end
      end

      non_localised_options.each do |key|
        current = options[key].to_s.strip
        next unless current.to_s.length > 0
        v.send("#{key}=", current) if NON_LOCALISED_VERSION_VALUES.include?(key)
        details.send("#{key}=", current) if NON_LOCALISED_APP_VALUES.include?(key)
      end

      v.release_on_approval = options[:automatic_release]
      v.auto_release_date = options[:auto_release_date] unless options[:auto_release_date].nil?
      v.toggle_phased_release(enabled: !!options[:phased_release]) unless options[:phased_release].nil?

      set_trade_representative_contact_information(v, options)
      set_review_information(v, options)
      set_app_rating(v, options)
      v.ratings_reset = options[:reset_ratings] unless options[:reset_ratings].nil?

      Helper.show_loading_indicator("Uploading metadata to App Store Connect")
      v.save!
      Helper.hide_loading_indicator
      begin
        details.save!
        UI.success("Successfully uploaded set of metadata to App Store Connect")
      rescue Spaceship::TunesClient::ITunesConnectError => e
        # This makes sure that we log invalid app names as user errors
        # If another string needs to be checked here we should
        # figure out a more generic way to handle these cases.
        if e.message.include?('App Name cannot be longer than 50 characters') || e.message.include?('The app name you entered is already being used')
          UI.error("Error in app name.  Try using 'individual_metadata_items' to identify the problem language.")
          UI.user_error!(e.message)
        else
          raise e
        end
      end
    end
def upload(options)
      return if options[:skip_metadata]
      # it is not possible to create new languages, because
      # :keywords is not write-able on published versions
      # therefore skip it.
      verify_available_languages!(options) unless options[:edit_live]

      app = options[:app]

      details = app.details
      if options[:edit_live]
        # not all values are editable when using live_version
        v = app.live_version(platform: options[:platform])
        localised_options = LOCALISED_LIVE_VALUES
        non_localised_options = NON_LOCALISED_LIVE_VALUES

        if v.nil?
          UI.message("Couldn't find live version, editing the current version on App Store Connect instead")
          v = app.edit_version(platform: options[:platform])
          # we don't want to update the localised_options and non_localised_options
          # as we also check for `options[:edit_live]` at other areas in the code
          # by not touching those 2 variables, deliver is more consistent with what the option says
          # in the documentation
        end
      else
        v = app.edit_version(platform: options[:platform])
        localised_options = (LOCALISED_VERSION_VALUES + LOCALISED_APP_VALUES)
        non_localised_options = (NON_LOCALISED_VERSION_VALUES + NON_LOCALISED_APP_VALUES)
      end

      individual = options[:individual_metadata_items] || []
      localised_options.each do |key|
        current = options[key]
        next unless current

        unless current.kind_of?(Hash)
          UI.error("Error with provided '#{key}'. Must be a hash, the key being the language.")
          next
        end

        current.each do |language, value|
          next unless value.to_s.length > 0
          strip_value = value.to_s.strip
          if individual.include?(key.to_s)
            upload_individual_item(app, v, language, key, strip_value)
          else
            v.send(key)[language] = strip_value if LOCALISED_VERSION_VALUES.include?(key)
            details.send(key)[language] = strip_value if LOCALISED_APP_VALUES.include?(key)
          end
        end
      end

      non_localised_options.each do |key|
        current = options[key].to_s.strip
        next unless current.to_s.length > 0
        v.send("#{key}=", current) if NON_LOCALISED_VERSION_VALUES.include?(key)
        details.send("#{key}=", current) if NON_LOCALISED_APP_VALUES.include?(key)
      end

      v.release_on_approval = options[:automatic_release]
      v.auto_release_date = options[:auto_release_date] unless options[:auto_release_date].nil?
      v.toggle_phased_release(enabled: !!options[:phased_release]) unless options[:phased_release].nil?

      set_trade_representative_contact_information(v, options)
      set_review_information(v, options)
      set_app_rating(v, options)
      v.ratings_reset = options[:reset_ratings] unless options[:reset_ratings].nil?

      Helper.show_loading_indicator("Uploading metadata to App Store Connect")
      v.save!
      Helper.hide_loading_indicator
      begin
        details.save!
        UI.success("Successfully uploaded set of metadata to App Store Connect")
      rescue Spaceship::TunesClient::ITunesConnectError => e
        # This makes sure that we log invalid app names as user errors
        # If another string needs to be checked here we should
        # figure out a more generic way to handle these cases.
        if e.message.include?('App Name cannot be longer than 50 characters') || e.message.include?('The app name you entered is already being used')
          UI.error("Error in app name.  Try using 'individual_metadata_items' to identify the problem language.")
          UI.user_error!(e.message)
        else
          raise e
        end
      end
    end
Make sure to call `load_from_filesystem` before calling upload