diff --git a/.rubocop.yml b/.rubocop.yml index f503e4b52d3..6fcc42f7945 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -79,6 +79,8 @@ Style/AndOr: Metrics/ClassLength: Max: 320 +Metrics/ModuleLength: + Max: 130 # Configuration parameters: AllowURI, URISchemes. Metrics/LineLength: diff --git a/fastlane_core/fastlane_core.gemspec b/fastlane_core/fastlane_core.gemspec index 7ee941aa80a..dd063eb2f81 100644 --- a/fastlane_core/fastlane_core.gemspec +++ b/fastlane_core/fastlane_core.gemspec @@ -31,6 +31,7 @@ Gem::Specification.new do |spec| spec.add_dependency 'plist', '~> 3.1' # needed for parsing provisioning profiles spec.add_dependency 'terminal-table', '~> 1.4.5' # options summary spec.add_dependency 'gh_inspector', '>= 1.0.1', '< 2.0.0' # search for issues on GitHub when something goes wrong + spec.add_dependency 'retriable', '~> 2.1' # simple DSL to retry failed code blocks spec.add_dependency 'credentials_manager', '>= 0.16.0', '< 1.0.0' # fastlane password manager diff --git a/fastlane_core/lib/fastlane_core/helper.rb b/fastlane_core/lib/fastlane_core/helper.rb index d69a787c788..7c8a25bd927 100644 --- a/fastlane_core/lib/fastlane_core/helper.rb +++ b/fastlane_core/lib/fastlane_core/helper.rb @@ -12,11 +12,34 @@ def self.log # Runs a given command using backticks (`) # and prints them out using the UI.command method - def self.backticks(command, print: true) - UI.command(command) if print - result = `#{command}` - UI.command_output(result) if print - return result + def self.backticks(command, print: true, retriable_options: {}) + require 'retriable' + tries = retriable_options[:tries] || 1 + timeout = retriable_options[:timeout] || 3600 + on_retry = proc do |exception, try, elapsed_time, next_interval| + if try < tries + UI.important("#{exception.class}: '#{exception.message}' - #{try} tries in #{elapsed_time} seconds and #{next_interval.to_i} seconds until the next try.") + end + end + + options = { + on: Timeout::Error, + tries: tries, + timeout: timeout, + base_interval: 0, + on_retry: on_retry + } + + begin + Retriable.retriable(options) do + UI.command(command) if print + result = `#{command}` + UI.command_output(result) if print + return result + end + rescue + UI.user_error!("Failed to execute: #{command}") + end end # @return true if the currently running program is a unit test diff --git a/fastlane_core/lib/fastlane_core/project.rb b/fastlane_core/lib/fastlane_core/project.rb index 43d29998b94..ce3dc4802a8 100644 --- a/fastlane_core/lib/fastlane_core/project.rb +++ b/fastlane_core/lib/fastlane_core/project.rb @@ -216,7 +216,14 @@ def build_xcodebuild_showbuildsettings_command def build_settings(key: nil, optional: true) unless @build_settings command = build_xcodebuild_showbuildsettings_command - @build_settings = Helper.backticks(command, print: false) + # Workaround for Xcode 8 problem: + # `xcodebuild -showBuildSettings` will hang up sometimes + # By retrying with specific timeout, this problem can be avoided + options = { + tries: (ENV['FASTLANE_XCODEBUILD_SETTINGS_TRIES'] || 3).to_i, + timeout: (ENV['FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT'] || 10).to_i + } + @build_settings = Helper.backticks(command, print: false, retriable_options: options) end begin