require 'fileutils'
fastlane_require 'xcodeproj'

skip_docs # Do not create fastlane/README.txt
opt_out_usage # Do not report analytics for runs of fastlane on the fastlane repo itself

# Test and Validate

desc "Runs the unit tests and other verifications for the fastlane repo"
lane :test do |options|
  validate_repo
end

desc "Verifies all tests pass and the current state of the repo is valid"
lane :validate_repo do
  version = local_version
  lint_source
  build_fastlane_gem(version: version)
  execute_tests
  validate_docs
  ensure_tool_name_formatting
  ensure_code_samples
  ensure_special_docs_code_samples
  ensure_code_snippets
  ensure_actions_config_items_formatting
end

desc "Verifies source code is in a good state"
lane :lint_source do
  # Verifying that no debug code is in the code base
  exclude_dirs = %w[.bundle vendor]
  ensure_no_debug_code(text: "binding\.(irb|pry)", extension: ".rb", exclude: "playground.rb", exclude_dirs: exclude_dirs) # debugging code
  ensure_no_debug_code(text: "# TODO", extension: ".rb", exclude_dirs: exclude_dirs) # TODOs
  ensure_no_debug_code(text: "now: ", extension: ".rb", exclude_dirs: exclude_dirs) # rspec focus
  ensure_no_debug_code(text: "<<<<<<<", extension: ".rb", exclude_dirs: exclude_dirs) # Merge conflict

  # spaceship and credentials_manager don't have access to fastlane_core
  ensure_no_debug_code(text: " UI\\.", path: "spaceship/lib", extension: ".rb", exclude_dirs: exclude_dirs)
  ensure_no_debug_code(text: " UI\\.", path: "credentials_manager/lib", extension: ".rb", exclude_dirs: exclude_dirs)

  rubocop(step_name: 'policekeeping_the_code_with_rubocop')

  Dir.chdir("..") do
    # If no shellcheck - warn and skip, otherwise shellcheck all .sh and .bash files
    if system('which shellcheck > /dev/null 2>&1')
      sh('git ls-files -z "*.sh" "*.bash" | grep -z -v "Pods-" | xargs -0 shellcheck')
    else
      UI.important("shellcheck not installed, skipping shell code style verification")
    end
  end
end

desc "Measure the execution time of the --help command"
lane :benchmark_help_command do
  Dir.chdir("..") do
    # install gnomon if not installed yet
    Actions.sh("echo foo | gnomon", log: true, error_callback: lambda { |result|
      if Helper.windows? || Helper.mac?
        sh('npm install -g gnomon')
      else
        sh('sudo npm install -g gnomon')
      end
    })
    cmd = Helper.windows? ? "cd bin && fastlane --help | gnomon" : "bin/fastlane --help | gnomon"
    3.times do
      content = sh(cmd, log: false)
      content.each_line do |line|
        UI.message("🏎️ '#{line.strip}'") if ["Total", "real", "user", "sys"].any? { |word| line.include?(word) }
      end
    end
  end
end

desc "Runs the tests"
lane :execute_tests do
  version = local_version # has to be outside of the `Dir.chdir`

  # Verify SSL connection is alright - https://github.com/fastlane/fastlane/issues/29710
  download(url: "https://rubygems.org/api/v1/gems/fastlane.json")

  # Verifying the --help command
  Dir.chdir("..") do
    cmd = Helper.windows? ? "cd bin && fastlane --help" : "PAGER=cat bin/fastlane --help"
    content = sh(cmd)
    ["--version", "https://fastlane.tools", "fastlane"].each do |current|
      UI.user_error!("--help missing information: '#{current}'") unless content.include?(current)
    end
  end

  benchmark_help_command

  # Run the tests
  build_fastlane_gem(version: version) unless File.file?("pkg/fastlane-#{version}.gem")
  Dir.chdir("..") do
    # Several processes rather than one. The suite is the same either way; a
    # worker just gets a slice of it. See fastlane#30189.
    sh("bundle", "exec", "rake", "test_parallel")
  end
end

# Release Management

desc "Increment the version number of this gem, after generating new Swift API"
lane :bump do |options|
  bump_type = options[:bump_type]
  is_automated_bump = !bump_type.nil?

  verify_env_variables
  verify_ruby_gem_credentials unless is_automated_bump
  ensure_git_branch(branch: "master")
  ensure_git_status_clean

  github_api_token = ENV["FL_GITHUB_RELEASE_API_TOKEN"]
  github_api_bearer = ENV["FL_GITHUB_RELEASE_API_BEARER"]

  if github_api_token.to_s.length == 0 && github_api_bearer.to_s.length == 0
    UI.user_error!("Please provide a GitHub API token using `FL_GITHUB_RELEASE_API_TOKEN` or `FL_GITHUB_RELEASE_API_BEARER`")
  end
  paths_for_commit = []

  version_file_path = "./fastlane/lib/fastlane/version.rb"
  last_version_file_path = File.absolute_path("../internal/CHANGELOG.latest.md")

  # Verify everything is in a consistent state
  latest_version = current_version
  local_version = version_get_podspec(path: version_file_path, require_variable_prefix: false)
  UI.user_error!("Version on RubyGems doesn't match local repo: #{latest_version} != #{local_version}") if latest_version != local_version

  changelog_text = show_changelog

  # Update CHANGELOG.lasest.md (which will be used to create the Release)
  File.write(last_version_file_path, changelog_text)

  bump_type ||= 'minor' if prompt(text: "New feature, method or API?", boolean: true)
  bump_type ||= 'patch'

  slug = "fastlane/fastlane"
  new_version = version_bump_podspec(path: version_file_path, bump_type: bump_type, require_variable_prefix: false)

  # Everything looks good, let's generate the Swift API
  generate_swift_api(fastlane_version: new_version)

  # Alright, now let's update our FastlaneSwiftRunner manifest
  generate_fastlane_swift_runner_manifest

  # Add all changed files to the version bump commit
  paths_for_commit += sh("cd .. && git status --porcelain fastlane/swift | sed s/^...//").each_line.to_a.map(&:strip)
  paths_for_commit << "Gemfile.lock"

  # Add CHANGELOG.latest
  paths_for_commit << last_version_file_path

  # Add version file path to change set
  paths_for_commit << version_file_path

  sh("bundle exec rake generate_team_table")
  paths_for_commit << "README.md"

  sh("bundle exec rake update_gem_spec_authors")
  paths_for_commit << "fastlane.gemspec"

  sh("git checkout -b 'version-bump-#{new_version}'")
  commit_message = "Version bump to #{new_version}"

  git_commit(path: paths_for_commit,
             message: commit_message)
  push_to_git_remote

  pr_body = ["Auto-generated by fastlane 🤖"]
  pr_body << "**Changes since release '#{latest_version}':**"
  pr_body << changelog_text
  pr_url = create_pull_request(
    api_bearer: github_api_bearer,
    api_token: github_api_token,
    repo: slug,
    title: commit_message,
    body: pr_body.join("\n\n")
  )

  if ENV['SLACK_URL']
    slack(
      channel: "action",
      default_payloads: [],
      message: "Version bump: #{pr_url} ✨",
      payload: {}
    )
  end

  # Revert to master branch
  unless is_automated_bump
    sh("git checkout master")
  end
end

desc "Generate the Swift api and test it"
lane :generate_swift_api do |options|
  Fastlane::VERSION = options[:fastlane_version] if options[:fastlane_version]

  require "../fastlane/lib/fastlane/swift_fastlane_api_generator.rb"
  swift_generator = Fastlane::SwiftFastlaneAPIGenerator.new
  generated_files = swift_generator.generate_swift
  Dir.chdir("swift/formatting") do
    sh("rake")
    UI.success("Done formatting fastlane.swift API")
  end
  UI.success("Done generating fastlane.swift API, ensuring it builds")
  gym(
    project: File.expand_path("swift/FastlaneSwiftRunner/FastlaneSwiftRunner.xcodeproj"),
    skip_package_ipa: true
  )
  sh("swift build")
  # Set by the "Validate Fastlane.swift generation" CI job; set it locally to run the same checks
  if ENV["FASTLANE_SWIFT_TESTS"]
    sh("swift test")
    sh("spec/swift/spm-runner/run.sh")
  end
  sh("rm -r ../.build")
  sh("rm ../FastlaneRunner")
  generated_files
end

desc "Generate the FastlaneSwiftRunner project manifest"
lane :generate_fastlane_swift_runner_manifest do |options|
  # Don't attempt to upgrade config files, like Fastfile.swift, Deliverfile.swift, etc...
  non_upgrade_set = TOOL_CONFIG_FILES.map { |tool_name| "#{tool_name}.swift" }.to_set

  swift_folder = File.expand_path("swift")
  runner_project_path = File.expand_path(File.join(swift_folder, "/FastlaneSwiftRunner/FastlaneSwiftRunner.xcodeproj"))
  project = Xcodeproj::Project.open(runner_project_path)

  swift_files = project.files
                       .reject { |file| file.name.nil? }
                       .reject { |file| non_upgrade_set.include?(file.name) }
                       .select { |file| file.name.end_with?(".swift") }

  # It is assumed that all Swift files we're including are contained in the root group, or 1 group deep
  swift_filenames_to_group_name = swift_files.each_with_object({}) { |file, object| object[file.name] = file.parent.name }

  manifest_path = File.expand_path(File.join(swift_folder, "/upgrade_manifest.json"))
  File.write(manifest_path, swift_filenames_to_group_name.to_json)

  UI.success("Done generating FastlaneSwiftRunner project manifest")
  manifest_path
end

desc "Creates a GitHub release with the gem as an asset"
lane :create_github_release do |options|
  # Git verification
  #
  ensure_git_status_clean
  ensure_git_branch(branch: 'master')

  # Verifying RubyGems version
  #
  version = local_version
  old_version = current_version
  if Gem::Version.new(version) <= Gem::Version.new(old_version)
    UI.user_error!("Version number #{version} was already deployed")
  end

  # Set the version into an ENV variable for GitHub Actions to read
  ENV['FASTLANE_RELEASE_VERSION'] = version
  File.write("../latest_fl_version.txt", version)

  github_username = ENV["RELEASE_GITHUB_USERNAME"] || "fastlane"
  repo_name = "#{github_username}/fastlane"

  # Preparing GitHub Release
  #
  github_release = get_github_release(url: repo_name, version: version, api_bearer: ENV['FASTLANE_RELEASE_API_BEARER'])
  gem_path = File.absolute_path("../pkg/fastlane-#{version}.gem")

  if (github_release || {}).fetch('body', '').length == 0
    # Validate repo and create gem
    validate_repo

    title = "Improvements"
    description = File.read("../internal/CHANGELOG.latest.md")

    github_release = set_github_release(
      repository_name: repo_name,
      name: [version, title].join(" "),
      tag_name: version,
      description: description,
      is_draft: false,
      upload_assets: [gem_path],
      api_bearer: ENV['FASTLANE_RELEASE_API_BEARER']
    )

    release_url = github_release['html_url']

    UI.success("New GitHub release has been created: #{release_url}")
  else
    UI.message("GitHub release has already been created")

    sleep(50) # GitHub rate limits are annoying
    download_github_release_gem(version: version)
  end

  sh "gem push --key github --host https://rubygems.pkg.github.com/#{github_username} #{gem_path}" unless options[:skip_github_packages]
end

lane :download_github_release_gem do |options|
  require 'open-uri'

  version = options[:version]

  github_username = ENV["RELEASE_GITHUB_USERNAME"] || "fastlane"

  github_release = get_github_release(url: "#{github_username}/fastlane", version: version, api_bearer: ENV['FASTLANE_RELEASE_API_BEARER'])
  assets = (github_release || {}).fetch('assets', [])

  gem_asset = assets.find { |a| a["name"].end_with?(".gem") }
  gem_asset_url = gem_asset["browser_download_url"]

  # Extract the file name from the URL
  file_name = File.basename(URI.parse(gem_asset_url).path)

  FileUtils.mkdir_p('../pkg')
  file_path = File.join('../pkg', file_name)

  # Download the file
  URI.parse(gem_asset_url).open do |remote_file|
    File.open(file_path, 'wb') do |local_file|
      local_file.write(remote_file.read)
    end
  end

  file_path
end

desc "Generate changelog from GitHub compare and PR data for mentioning GitHub usernames in release notes"
private_lane :github_changelog do |options|
  old_version = options[:old_version] || current_version
  path = "/repos/fastlane/fastlane/compare/#{old_version}...HEAD"

  # Get all commits from previous version (tag) to HEAD
  resp = github_api(path: path)
  body = JSON.parse(resp[:body])
  commits = body["commits"].reverse

  sleep_time = ENV['FL_CHANGELOG_SLEEP'].to_i

  formatted = commits.map do |commit|
    if sleep_time > 0
      UI.message("Sleeping for #{sleep_time} second(s) to get changelog")
      sleep(sleep_time)
    end

    # Default to commit message info
    message = commit["commit"]["message"].lines.first.strip
    name = commit["commit"]["author"]["name"]
    username = commit["author"]&.[]("login")

    # Get pull request associate with commit message
    sha = commit["sha"]
    pr_resp = github_api(path: "/search/issues?q=repo:fastlane/fastlane+is:pr+base:master+SHA:#{sha}")
    body = JSON.parse(pr_resp[:body])
    items = body["items"]

    item_infos = items.map do |item|
      message = "#{item['title']} (##{item['number']})"
      username = item["user"]["login"]

      [message, username]
    end

    # We may find 0 PRs, 1 PR, n+ PRs and we may be interactive or not. Self recovery exists in non-interactive
    # User selection is required in interactive
    if item_infos.size == 0
      if !UI.interactive?
        UI.important("No pull request found for commit #{sha}, falling back to the commit message for the changelog entry")
      elsif UI.confirm("Error generating changelog. No pull request found for commit #{sha}. Skip?")
        next
      else
        UI.user_error!("Cannot generate changelog. No pull request found for commit #{sha}")
      end
    elsif item_infos.size == 1
      message = item_infos[0][0]
      username = item_infos[0][1]
    elsif !UI.interactive?
      UI.important("Multiple pull requests found for commit #{sha}: #{item_infos.map { |info| info[0] }.join(', ')} - using the first one")
      message = item_infos[0][0]
      username = item_infos[0][1]
    else
      options = item_infos.map do |item|
        "#{item[1]} - #{item[0]})"
      end

      option = UI.select("Which changelog?", options)
      index = options.index(option)

      message = item_infos[index][0]
      username = item_infos[index][1]
    end

    username ? "* #{message} via #{name} (@#{username})" : "* #{message} via #{name}"
  end.compact.join("\n")

  formatted
end

desc "Print out the changelog since the last tagged release and open the GitHub page with the changes"
lane :show_changelog do |options|
  old_version = options[:old_version] || current_version

  changes = github_changelog(old_version: old_version)

  github_diff_url = "https://github.com/fastlane/fastlane/compare/#{old_version}...master"
  sh("open #{github_diff_url}")

  puts("Changes since release #{old_version}:\n\n#{changes.cyan}")
  changes # return the text without the modified colors
end

# Docs

desc "Validates the docs still are good"
lane :validate_docs do
  # Validate docs content
  ensure_tool_name_formatting
  ensure_markdown_links
  ensure_code_samples
  ensure_special_docs_code_samples
  ensure_code_snippets
  # (Validate action documentation)
  ensure_actions_config_items_formatting

  # Test if generating the docs is successful
  clone_docs do
    generate_markdown_docs(target_path: ".")
  end

  # Verify docs are still working
  verify_docs
end

desc "Makes sure the tests on https://docs.fastlane.tools still work with the latest version"
lane :verify_docs do
  clone_docs do
    Bundler.with_clean_env do
      puts(`sed -i -e "s/activate_bin_path/bin_path/g" $(which bundle)`) # workaround for bundler https://github.com/bundler/bundler/issues/4602#issuecomment-233619696
      sh("bundle install")
      sh("bundle exec fastlane test skip_building:true") # skip_building since we don't have a proper python environment set up
    end
  end
end

desc "Update the actions.md on https://docs.fastlane.tools"
desc "This will also automatically submit a pull request to fastlane/docs"
lane :update_docs do |options|
  debug = options[:debug]
  version = options[:version] || current_version

  puts("Starting docs update for version #{version} 🚀")

  unless debug
    verify_env_variables
    ensure_actions_config_items_formatting
  end

  template_path = File.expand_path("./assets/render_plugin.md.erb")
  clone_docs(debug: debug) do
    plugin_scores_cache_path = File.expand_path("./plugin_scores_cache.yml")
    docs_path = generate_markdown_docs(target_path: ".")
    yml_path = File.expand_path("./mkdocs.yml")
    actions_md_path = File.expand_path(File.join(docs_path, "docs/generated/actions.md"))
    team_table_md_path = File.expand_path(File.join(docs_path, "docs/generated/team-table.md"))

    action_docs_dir = File.join(docs_path, "docs/generated/actions")
    custom_assets_dir = File.join(docs_path, "docs/img/actions")

    # We want to align fastlane/docs "fastlane" version with the latest released version.
    # This ensures documentation tests leverage the latest action parameters and code samples.
    unless options[:skip_bundle_update]
      Bundler.with_unbundled_env do
        sh('bundle', 'update', 'fastlane')
      end
    end

    if `git status --porcelain`.length == 0
      UI.success("No changes in the actions.md ✅")
    else
      # Create a new branch
      sh("git checkout -b 'update-actions-md-#{Time.now.to_i}'") unless debug
      plugins_path = "docs/generated/available-plugins.md"
      unless options[:skip_plugin_scores]
        plugin_scores(template_path: template_path,
                        output_path: plugins_path,
                         cache_path: plugin_scores_cache_path)
      end

      next if debug

      Dir.chdir("fastlane") do # this is an assumption of fastlane, that we have to .. when shelling out
        # Commit the changes
        changes_to_commit = [plugin_scores_cache_path, actions_md_path, team_table_md_path, "Gemfile.lock", plugins_path, yml_path, action_docs_dir, custom_assets_dir]
        git_add(path: changes_to_commit)
        git_commit(path: changes_to_commit,
                message: "Update docs for latest fastlane release #{version} (actions.md, available-plugins.md) 🚀")
        # Push them to the git remote
        push_to_git_remote

        # Submit the pull request
        pr_url = create_pull_request(
          api_token: ENV["FL_GITHUB_RELEASE_API_TOKEN"],
          repo: "fastlane/docs",
          title: "[Bot] Update docs for latest fastlane release #{version} (actions.md, available-plugins.md) 🚀",
          body: "Auto-generated by _fastlane_ bot 🤖"
        )
        UI.success("Successfully submitted a pull request to fastlane/docs: #{pr_url} 🚀")
      end
    end
  end
end

def clone_docs(debug: false)
  if debug
    clone_url = ENV['FASTLANE_DOCS_CLONE_URL'] || "../../docs"

    Dir.chdir(clone_url) do
      yield
    end
  else
    require 'tmpdir'
    git_url = ENV['FASTLANE_DOCS_GIT_URL'] || "https://github.com/fastlane/docs"

    Dir.mktmpdir("fl_clone") do |tmp_dir|
      Dir.chdir(tmp_dir) do
        sh("git clone #{git_url} --depth=1")
        Dir.chdir("docs") do
          yield
        end
      end
    end
  end
end

def generate_markdown_docs(target_path: nil)
  require 'fastlane/documentation/markdown_docs_generator'
  Fastlane::MarkdownDocsGenerator.new.generate!(target_path: File.expand_path(target_path))
  return target_path
end

# Exception Handling

error do |lane, exception|
  if ENV['SLACK_URL']
    slack(channel: "testing", message: exception.to_s, success: false)
  end
end

# Helper

desc "Ensure all the requirement environment variables are provided"
desc "this way the deployment script will fail early (and often)"
private_lane :verify_env_variables do
  ensure_env_vars(env_vars: ['GITHUB_USER_NAME', 'GITHUB_API_TOKEN'])
end

desc "Confirm RubyGem credentials are set up correctly"
private_lane :verify_ruby_gem_credentials do
  UI.user_error!("You're not logged in to RubyGems. Log in using `gem push` if using RubyGems < 2.7.0 or `gem signin` if using RubyGems >=2.7.0") unless File.file?(File.expand_path("~/.gem/credentials"))
end

desc "Builds the fastlane gem"
private_lane :build_fastlane_gem do |options|
  version = options[:version]

  Dir.chdir("..") do
    begin
      sh("bundle check")
    rescue => e
      UI.message("bundle check failed (#{e.message.strip}), running bundle install")
      sh("bundle install")
    end

    sh("rake", "install")

    # Ensure the file size of the fastlane gem is below 1.5 MB
    size_in_mb = File.size("pkg/fastlane-#{version}.gem").to_f / (1024 * 1024 * 1.5)
    UI.user_error!("fastlane gem is above 1.5 MB, make sure no additional resources are included by mistake") if size_in_mb > 1
  end
end

desc "Get the local version number per version.rb"
private_lane :local_version do
  require_relative "../fastlane/lib/fastlane/version.rb"

  Fastlane::VERSION
end

desc "Get the version number of the last release"
private_lane :current_version do
  puts("Checking the latest version on RubyGems")
  download(url: "https://rubygems.org/api/v1/gems/fastlane.json")["version"]
end

desc "Ensure the correct formatting for the fastlane tools"
private_lane :ensure_tool_name_formatting do
  require_relative "../internal/docs/tool_name_formatting"
  UI.message("🕗  Verifying tool name formatting...")
  require 'fastlane/tools'
  errors = []
  Dir.chdir("..") do
    Dir["**/*.md"].each do |path|
      next if path == "internal/CHANGELOG.latest.md"
      next if path.start_with?('vendor/', '.bundle/')
      helper = Fastlane::Internal::Docs::ToolNameFormatting.new(path: path, is_documenting_invalid_examples: path == 'docs/CONTRIBUTING.md')
      errors += helper.find_tool_name_formatting_errors
    end
  end
  errors.each { |a| UI.error(a) }
  UI.user_error!("Invalid formatting of one of the fastlane tools") unless errors.empty?
  UI.success("✅  fastlane tools formatting is correct")
end

private_lane :ensure_markdown_links do
  require_relative "../internal/docs/markdown_links"
  UI.message("🕗  Verifying links in Markdown files...")
  errors = []
  Dir.chdir("..") do
    Dir["**/*.md"].each do |path|
      next if path.start_with?('vendor/', '.bundle/') || path.include?('/fixtures/')
      next if File.basename(path) == "CHANGELOG.latest.md"
      errors += Fastlane::Internal::Docs::MarkdownLinks.new(path: path).errors
    end
  end
  errors.each { |a| UI.error(a) }
  UI.user_error!("Broken links in Markdown files") unless errors.empty?
  UI.success("✅  Markdown links are valid")
end

# Run the code samples, yo
private_lane :ensure_code_samples do
  UI.message("🕗  Verifying all action code samples work with the current fastlane release")
  all_content = ""
  count_actions = 0
  ActionsList.all_actions do |action|
    all_content += "```ruby\n"
    all_content += action.example_code.map { |code| code.gsub(/^\s+/, '') }.join("\n") if action.example_code
    all_content += "\n```"
    count_actions += 1
  end
  test_sample_code(content: all_content) # to not have to call the action 200 times
  UI.success("✅  All fastlane action code samples (from #{count_actions} actions) work as expected")
end

# Run the other code samples, yo
private_lane :ensure_special_docs_code_samples do
  UI.message("🕗  Verifying all action special docs (`fastlane/lib/fastlane/actions/docs`) code samples work with the latest fastlane release")
  all_content = ""
  files = Dir["../fastlane/lib/fastlane/actions/docs/*.md"]
  files.each do |special_docs_page_path|
    all_content += File.read(special_docs_page_path)
  end
  test_sample_code(content: all_content) # to not have to call the action 200 times
  UI.success("✅  All fastlane action special docs code samples (from #{files.length} files) work as expected")
end

private_lane :ensure_code_snippets do
  UI.message("🕗  Verifying all code snippets are correctly formatted")

  shell_commands_start = '((bundle|fastlane)[ ]|[A-Z_]+=)'
  errors = []

  require 'fastlane/documentation/markdown_docs_generator'

  Fastlane::MarkdownDocsGenerator.new.actions_md_contents.each do |action_name, content|
    errors << "Use '```no-highlight ↵ <code snippet> ↵ ```' instead of '```<language> ↵ <code snippet> ↵ ```' for code snippets for action '#{action_name}'" if content =~ /```(?<!no-higlight)\w+\s*#{shell_commands_start}/
    errors << "Use '```no-highlight ↵ <code snippet> ↵ ```' instead of '    <code snippet>' for code snippets for action '#{action_name}'" if content =~ /^[ ]{4}#{shell_commands_start}/
  end

  errors.each { |a| UI.error(a) }

  UI.user_error!("Invalid formatting of one of the code snippets") unless errors.empty?
  UI.success("✅  All code snippets are formatted as expected")
end

private_lane :ensure_actions_config_items_formatting do
  UI.message("🕗  Verifying actions' config items formatting...")

  errors = []

  require 'fastlane_core/configuration/config_item'

  ActionsList.all_actions do |action|
    next if action.available_options.nil?

    action.available_options.each do |option|
      next unless option.kind_of?(FastlaneCore::ConfigItem)

      errors << "Remove the '\\n' from the config item ':#{option.key}' of the action '#{action.action_name}'" if option.description && option.description =~ /\n|\\n/
    end
  end

  errors.each { |a| UI.error(a) }

  UI.user_error!("Invalid formatting of one of the actions' config items") unless errors.empty?
  UI.success("✅  fastlane actions' config items formatting is correct")
end
