module Fastlane module Actions class EnsureXcodeVersionAction < Action def self.run(params) Actions.verify_gem!('xcode-install') required_version = params[:version] strict = params[:strict] if required_version.to_s.length == 0 # The user didn't provide an Xcode version, let's see # if the current project has a `.xcode-version` file # # The code below can be improved to also consider # the directory of the Xcode project xcode_version_paths = Dir.glob(".xcode-version") if xcode_version_paths.first UI.verbose("Loading required version from #{xcode_version_paths.first}") required_version = File.read(xcode_version_paths.first).strip else UI.user_error!("No version: provided when calling the `ensure_xcode_version` action") end end selected_version = sh("xcversion selected").match(/^Xcode (.*)$/)[1] begin selected_version = Gem::Version.new(selected_version) required_version = Gem::Version.new(required_version) rescue ArgumentError => ex UI.user_error!("Invalid version number provided, make sure it's valid: #{ex}") end if strict == true if selected_version == required_version success(selected_version) else error(selected_version, required_version) end else required_version_numbers = required_version.to_s.split(".") selected_version_numbers = selected_version.to_s.split(".") required_version_numbers.each_with_index do |required_version_number, index| selected_version_number = selected_version_numbers[index] next unless required_version_number != selected_version_number error(selected_version, required_version) break end success(selected_version) end end def self.success(selected_version) UI.success("Selected Xcode version is correct: #{selected_version}") end def self.error(selected_version, required_version) UI.message("Selected Xcode version is not correct: #{selected_version}. You expected #{required_version}.") UI.message("To correct this, use: `xcode_select(version: #{required_version})`.") UI.user_error!("Selected Xcode version doesn't match your requirement.\nExpected: Xcode #{required_version}\nActual: Xcode #{selected_version}\n") end ##################################################### # @!group Documentation ##################################################### def self.description "Ensure the right version of Xcode is used" end def self.details [ "If building your app requires a specific version of Xcode, you can invoke this command before using gym.", "For example, to ensure that a beta version of Xcode is not accidentally selected to build, which would make uploading to TestFlight fail.", "You can either manually provide a specific version using `version: ` or you make use of the `.xcode-version` file.", "Using the `strict` parameter, you can either verify the full set of version numbers strictly (i.e. `11.3.1`) or only a subset of them (i.e. `11.3` or `11`)." ].join("\n") end def self.available_options [ FastlaneCore::ConfigItem.new(key: :version, env_name: "FL_ENSURE_XCODE_VERSION", description: "Xcode version to verify that is selected", is_string: true, optional: true), FastlaneCore::ConfigItem.new(key: :strict, description: "Should the version be verified strictly (all 3 version numbers), or matching only the given version numbers (i.e. `11.3` == `11.3.x`)", type: Boolean, default_value: true) ] end def self.output [ ['FL_ENSURE_XCODE_VERSION', 'Xcode version to verify that is selected'] ] end def self.return_value end def self.authors ["JaviSoto", "KrauseFx"] end def self.example_code [ 'ensure_xcode_version(version: "7.2")' ] end def self.category :building end def self.is_supported?(platform) [:ios, :mac].include?(platform) end end end end