deliver/lib/deliver/loader.rb in fastlane_hotfix-2.165.1 vs deliver/lib/deliver/loader.rb in fastlane_hotfix-2.187.0

- old
+ new

@@ -1,9 +1,8 @@ -require 'fastlane_core/languages' -require 'spaceship/tunes/tunes' - require_relative 'module' +require_relative 'app_screenshot' +require_relative 'app_screenshot_validator' require_relative 'upload_metadata' require_relative 'languages' module Deliver module Loader @@ -11,43 +10,146 @@ # through it as well searching for language folders. APPLE_TV_DIR_NAME = "appleTV".freeze IMESSAGE_DIR_NAME = "iMessage".freeze DEFAULT_DIR_NAME = "default".freeze + EXPANDABLE_DIR_NAMES = [APPLE_TV_DIR_NAME, IMESSAGE_DIR_NAME].freeze SPECIAL_DIR_NAMES = [APPLE_TV_DIR_NAME, IMESSAGE_DIR_NAME, DEFAULT_DIR_NAME].freeze # Some exception directories may exist from other actions that should not be iterated through SUPPLY_DIR_NAME = "android".freeze FRAMEIT_FONTS_DIR_NAME = "fonts".freeze META_DIR_NAMES = UploadMetadata::ALL_META_SUB_DIRS.map(&:downcase) - EXCEPTION_DIRECTORIES = (META_DIR_NAMES << SUPPLY_DIR_NAME << FRAMEIT_FONTS_DIR_NAME).freeze + EXCEPTION_DIRECTORIES = (META_DIR_NAMES << SUPPLY_DIR_NAME << FRAMEIT_FONTS_DIR_NAME).freeze - def self.language_folders(root, ignore_validation) - folders = Dir.glob(File.join(root, '*')) + # A class that represents language folder under screenshots or metadata folder + class LanguageFolder + attr_reader :path - # 2020-08-24 - Available locales are not available as an endpoint in App Store Connect - # Update with Spaceship::Tunes.client.available_languages.sort (as long as endpoint is avilable) - available_languages = Deliver::Languages::ALL_LANGUAGES + # @return [String] A normalized language name that corresponds to the directory's name + attr_reader :language - allowed_directory_names_with_case = (available_languages + SPECIAL_DIR_NAMES) - allowed_directory_names = allowed_directory_names_with_case.map(&:downcase).freeze + def self.available_languages + # 2020-08-24 - Available locales are not available as an endpoint in App Store Connect + # Update with Spaceship::Tunes.client.available_languages.sort (as long as endpoint is avilable) + Deliver::Languages::ALL_LANGUAGES + end - selected_folders = folders.select do |path| - File.directory?(path) && allowed_directory_names.include?(File.basename(path).downcase) - end.sort + def self.allowed_directory_names_with_case + available_languages + SPECIAL_DIR_NAMES + end - # Gets list of folders that are not supported languages - rejected_folders = folders.select do |path| - normalized_path = File.basename(path).downcase - File.directory?(path) && !allowed_directory_names.include?(normalized_path) && !EXCEPTION_DIRECTORIES.include?(normalized_path) - end.sort + # @param path [String] A directory path otherwise this initializer fails + # @param nested [Boolan] Whether given path is nested of another special directory. + # This affects `expandable?` to return `false` when this set to `true`. + def initialize(path, nested: false) + raise(ArgumentError, "Given path must be a directory path - #{path}") unless File.directory?(path) + @path = path + @language = self.class.available_languages.find { |lang| basename.casecmp?(lang) } + @nested = nested + end + def nested? + @nested + end + + def valid? + self.class.allowed_directory_names_with_case.any? { |name| name.casecmp?(basename) } + end + + def expandable? + !nested? && EXPANDABLE_DIR_NAMES.any? { |name| name.casecmp?(basename) } + end + + def skip? + EXCEPTION_DIRECTORIES.map(&:downcase).include?(basename.downcase) + end + + def file_paths(extensions = '{png,jpg,jpeg}') + Dir.glob(File.join(path, "*.#{extensions}"), File::FNM_CASEFOLD).sort + end + + def framed_file_paths(extensions = '{png,jpg,jpeg}') + Dir.glob(File.join(path, "*_framed.#{extensions}"), File::FNM_CASEFOLD).sort + end + + def basename + File.basename(@path) + end + end + + # Returns the list of valid app screenshot. When detecting invalid screenshots, this will cause an error. + # + # @param root [String] A directory path + # @param ignore_validation [String] Set false not to raise the error when finding invalid folder name + # @return [Array<AppScreenshot>] The list of AppScreenshot that exist under given `root` directory + def self.load_app_screenshots(root, ignore_validation) + screenshots = language_folders(root, ignore_validation, true).flat_map do |language_folder| + paths = if language_folder.framed_file_paths.count > 0 + UI.important("Framed screenshots are detected! 🖼 Non-framed screenshot files may be skipped. 🏃") + # watchOS screenshots can be picked up even when framed ones were found since frameit doesn't support watchOS screenshots + framed_or_watch, skipped = language_folder.file_paths.partition { |path| path.downcase.include?('framed') || path.downcase.include?('watch') } + skipped.each { |path| UI.important("🏃 Skipping screenshot file: #{path}") } + framed_or_watch + else + language_folder.file_paths + end + paths.map { |path| AppScreenshot.new(path, language_folder.language) } + end + + errors = [] + valid_screenshots = screenshots.select { |screenshot| Deliver::AppScreenshotValidator.validate(screenshot, errors) } + + errors_to_skip, errors_to_crash = errors.partition(&:to_skip) + + unless errors_to_skip.empty? + UI.important("🏃 Screenshots to be skipped are detected!") + errors_to_skip.each { |error| UI.message(error) } + end + + unless errors_to_crash.empty? + UI.important("🚫 Invalid screenshots were detected! Here are the reasons:") + errors_to_crash.each { |error| UI.error(error) } + UI.user_error!("Canceled uploading screenshots. Please check the error messages above and fix the screenshots.") + end + + valid_screenshots + end + + # Returns the list of language folders + # + # @param roort [String] A directory path to get the list of language folders + # @param ignore_validation [Boolean] Set false not to raise the error when finding invalid folder name + # @param expand_sub_folders [Boolean] Set true to expand special folders; such as "iMessage" to nested language folders + # @return [Array<LanguageFolder>] The list of LanguageFolder whose each of them + def self.language_folders(root, ignore_validation, expand_sub_folders = false) + folders = Dir.glob(File.join(root, '*')) + .select { |path| File.directory?(path) } + .map { |path| LanguageFolder.new(path, nested: false) } + .reject(&:skip?) + + selected_folders, rejected_folders = folders.partition(&:valid?) + if !ignore_validation && !rejected_folders.empty? - rejected_folders = rejected_folders.map { |path| File.basename(path) } + rejected_folders = rejected_folders.map(&:basename) UI.user_error!("Unsupported directory name(s) for screenshots/metadata in '#{root}': #{rejected_folders.join(', ')}" \ - "\nValid directory names are: #{allowed_directory_names_with_case}" \ + "\nValid directory names are: #{LanguageFolder.allowed_directory_names_with_case}" \ "\n\nEnable 'ignore_language_directory_validation' to prevent this validation from happening") + end + + # Expand selected_folders for the special directories + if expand_sub_folders + selected_folders = selected_folders.flat_map do |folder| + if folder.expandable? + Dir.glob(File.join(folder.path, '*')) + .select { |p| File.directory?(p) } + .map { |p| LanguageFolder.new(p, nested: true) } + .select(&:valid?) + else + folder + end + end end selected_folders end end