class FastlaneCore::UpdateChecker

Verifies, the user runs the latest version of this gem

Constants

UPDATE_URL

This web service is fully open source: github.com/fastlane/refresher

Public Class Methods

android_app_identifier(args, gem_name) click to toggle source

(optional) Returns the app identifier for the current tool supply and screengrab use different param names and env variable patterns so we have to special case here example:

supply --skip_upload_screenshots -a beta -p com.test.app should return com.test.app
screengrab -a com.test.app should return com.test.app
# File lib/fastlane_core/update_checker/update_checker.rb, line 144
def self.android_app_identifier(args, gem_name)
  app_identifier = nil
  # args example: ["-a", "com.krausefx.app"]
  args.each_with_index do |current, index|
    if android_app_identifier_arg?(gem_name, current)
      app_identifier = args[index + 1] if args.count > index
      break
    end
  end

  app_identifier ||= ENV["SUPPLY_PACKAGE_NAME"] if FastlaneCore::Env.truthy?("SUPPLY_PACKAGE_NAME")
  app_identifier ||= ENV["SCREENGRAB_APP_PACKAGE_NAME"] if FastlaneCore::Env.truthy?("SCREENGRAB_APP_PACKAGE_NAME")
  app_identifier ||= CredentialsManager::AppfileConfig.try_fetch_value(:package_name)

  # Add Android prefix to prevent collisions if there is an iOS app with the same identifier
  app_identifier ? "android_project_#{app_identifier}" : nil
rescue
  nil # we don't want this method to cause a crash
end
android_app_identifier_arg?(gem_name, arg) click to toggle source
# File lib/fastlane_core/update_checker/update_checker.rb, line 164
def self.android_app_identifier_arg?(gem_name, arg)
  return arg == "--package_name" ||
         arg == "--app_package_name" ||
         (arg == '-p' && gem_name == 'supply') ||
         (arg == '-a' && gem_name == 'screengrab')
end
fetch_latest(url) click to toggle source
# File lib/fastlane_core/update_checker/update_checker.rb, line 101
def self.fetch_latest(url)
  JSON.parse(Excon.post(url).body).fetch("version", nil)
end
finished_running(gem_name) click to toggle source
# File lib/fastlane_core/update_checker/update_checker.rb, line 105
def self.finished_running(gem_name)
  return if ENV["FASTLANE_OPT_OUT_USAGE"]

  time = (Time.now - @start_time).to_i
  url = UPDATE_URL + "time/#{gem_name}"
  url += "?time=#{time}"
  url += "&ci=1" if Helper.ci?
  url += "&gem=1" if Helper.rubygems?
  url += "&bundler=1" if Helper.bundler?
  url += "&mac_app=1" if Helper.mac_app?
  url += "&standalone=1" if Helper.contained_fastlane?
  url += "&homebrew=1" if Helper.homebrew?

  Excon.post(url)
end
generate_fetch_url(gem_name) click to toggle source

Generate the URL on the main thread (since we're switching directory)

# File lib/fastlane_core/update_checker/update_checker.rb, line 88
def self.generate_fetch_url(gem_name)
  url = UPDATE_URL + gem_name
  params = {}
  params["ci"] = "1" if Helper.is_ci?

  project_hash = p_hash(ARGV, gem_name)
  params["p_hash"] = project_hash if project_hash
  params["platform"] = @platform if @platform # this has to be called after `p_hash`

  url += "?" + URI.encode_www_form(params) if params.count > 0
  return url
end
ios_app_identifier(args) click to toggle source

(optional) Returns the app identifier for the current tool

# File lib/fastlane_core/update_checker/update_checker.rb, line 122
def self.ios_app_identifier(args)
  # args example: ["-a", "com.krausefx.app", "--team_id", "5AA97AAHK2"]
  args.each_with_index do |current, index|
    if current == "-a" || current == "--app_identifier"
      return args[index + 1] if args.count > index
    end
  end

  ["FASTLANE", "DELIVER", "PILOT", "PRODUCE", "PEM", "SIGH", "SNAPSHOT", "MATCH"].each do |current|
    return ENV["#{current}_APP_IDENTIFIER"] if FastlaneCore::Env.truthy?("#{current}_APP_IDENTIFIER")
  end

  return CredentialsManager::AppfileConfig.try_fetch_value(:app_identifier)
rescue
  nil # we don't want this method to cause a crash
end
p_hash(args, gem_name) click to toggle source

To not count the same projects multiple time for the number of launches More information: github.com/fastlane/refresher Use the `FASTLANE_OPT_OUT_USAGE` variable to opt out The resulting value is e.g. ce12f8371df11ef6097a83bdf2303e4357d6f5040acc4f76019489fa5deeae0d

# File lib/fastlane_core/update_checker/update_checker.rb, line 175
def self.p_hash(args, gem_name)
  return nil if FastlaneCore::Env.truthy?("FASTLANE_OPT_OUT_USAGE")
  require 'credentials_manager'

  # check if this is an android project first because some of the same params exist for iOS and Android tools
  app_identifier = android_app_identifier(args, gem_name)
  @platform = nil # since have a state in-between runs
  if app_identifier
    @platform = :android
  else
    app_identifier = ios_app_identifier(args)
    @platform = :ios if app_identifier
  end

  if app_identifier
    return Digest::SHA256.hexdigest("p#{app_identifier}fastlan3_SAlt") # hashed + salted the bundle identifier
  end

  return nil
rescue
  return nil
end
server_results() click to toggle source
# File lib/fastlane_core/update_checker/update_checker.rb, line 27
def self.server_results
  @results ||= {}
end
show_update_message(gem_name, current_version) click to toggle source

Show a message to the user to update to a new version of fastlane (or a sub-gem) Use this method, as this will detect the current Ruby environment and show an appropriate message to the user

# File lib/fastlane_core/update_checker/update_checker.rb, line 53
def self.show_update_message(gem_name, current_version)
  available = server_results[gem_name]
  puts ""
  puts '#######################################################################'.green
  if available
    puts "# #{gem_name} #{available} is available. You are on #{current_version}.".green
  else
    puts "# An update for #{gem_name} is available. You are on #{current_version}.".green
  end
  puts "# It is recommended to use the latest version.".green
  puts "# Please update using `#{self.update_command(gem_name: gem_name)}`.".green

  puts "# To see what's new, open https://github.com/fastlane/#{gem_name}/releases.".green if FastlaneCore::Env.truthy?("FASTLANE_HIDE_CHANGELOG")

  if !Helper.bundler? && !Helper.contained_fastlane? && Random.rand(5) == 1
    # We want to show this message from time to time, if the user doesn't use bundler, nor bundled fastlane
    puts '#######################################################################'.green
    puts "# Run `sudo gem cleanup` from time to time to speed up fastlane".green
  end
  puts '#######################################################################'.green
  Changelog.show_changes(gem_name, current_version) unless FastlaneCore::Env.truthy?("FASTLANE_HIDE_CHANGELOG")
end
show_update_status(gem_name, current_version) click to toggle source
# File lib/fastlane_core/update_checker/update_checker.rb, line 36
def self.show_update_status(gem_name, current_version)
  fork do
    begin
      finished_running(gem_name)
    rescue
      # we don't want to show a stack trace if something goes wrong
    end
  end

  if update_available?(gem_name, current_version)
    show_update_message(gem_name, current_version)
  end
end
start_looking_for_update(gem_name) click to toggle source
# File lib/fastlane_core/update_checker/update_checker.rb, line 12
def self.start_looking_for_update(gem_name)
  return if Helper.is_test?
  return if FastlaneCore::Env.truthy?("FASTLANE_SKIP_UPDATE_CHECK")

  @start_time = Time.now

  url = generate_fetch_url(gem_name)
  Thread.new do
    begin
      server_results[gem_name] = fetch_latest(url)
    rescue
    end
  end
end
update_available?(gem_name, current_version) click to toggle source
# File lib/fastlane_core/update_checker/update_checker.rb, line 31
def self.update_available?(gem_name, current_version)
  latest = server_results[gem_name]
  return (latest and Gem::Version.new(latest) > Gem::Version.new(current_version))
end
update_command(gem_name: "fastlane") click to toggle source

The command that the user should use to update their mac

# File lib/fastlane_core/update_checker/update_checker.rb, line 77
def self.update_command(gem_name: "fastlane")
  if Helper.bundler?
    "bundle update #{gem_name.downcase}"
  elsif Helper.contained_fastlane?
    "fastlane update_fastlane"
  else
    "sudo gem update #{gem_name.downcase}"
  end
end