Skip to content

Commit

Permalink
[action] add xcodes action, deprecating xcversion and `xcode-inst…
Browse files Browse the repository at this point in the history
…all` (#20672)

* Move helpers to a separate and centralized file.

* Deprecate 'xcode-install' gem (the project has been sunset), adding 'xcodes' action to replace the 'xcode_install' one.

* Add 'select_only' option to 'xcodes', deprecating 'xcversion' action.

* Update documentation to state it also supports .xcode-version file.

* Add TODO for a future improvement.

* Lint.

* Fix link.

* Remove TODO comment.

* Lint.

* Fix typo in error being raised.

* Add ability to select Xcode just for current build, without sudo permissions.
- Depends on XcodesOrg/xcodes#220

* Update documentation to point to 'xcodes' instead.

* Update xcodes link.

* Apply suggestions from code review.

Co-authored-by: Iulian Onofrei <5748627+revolter@users.noreply.github.com>

* Change "#" by "." in spec descriptions, to indicate class methods.

* Apply suggestion from code review.

* Improve documentation around requirements to run xcodes.

* Remove necessity to authenticate with Apple servers since xcodes v1.0.0.

* Use new --update and --selected arguments, which require xcodes v1.1.0.

* Fix comparing Gem::Version with String error

* Remove unused local variable

* Split long lines

* Fix lint issues

* Address style comments.

* Remove superfluous trailing slash

This makes it consistent with the rest of the codebase.

* Fix unsafe paths concatenation

* Fix some WWDR certificates test failure

This was caused by the fact that that test calls a method that uses `$?`
(https://stackoverflow.com/a/6834572/865175), which, somehow, (I think)
was executed right after a call to XcodesHelper's
`find_xcodes_binary_path` method. This latter one returned a non-zero
code, because `xcodes` can't be installed on Ubuntu, which got picked up
by the certificates return code check, so it treated its actual command
as failed.

* Replace `Actions.sh` call with a native one

(reverted from commit 11bd750)

* Fix error-prone shell command status check

In the case of the tests, `$?` was not mocked, so it looks like it was
indeed just a coincidence that it worked. Meaning, some other shell
command that somehow was actually executed (so it wasn't stubbed)
succeeded, which caused the `$?.success?` to succeed too.

Co-authored-by: Iulian Onofrei <5748627+revolter@users.noreply.github.com>
Co-authored-by: Iulian Onofrei <iulian.onofrei@yahoo.com>
  • Loading branch information
3 people committed Nov 12, 2022
1 parent 7de28ca commit 4b30089
Show file tree
Hide file tree
Showing 14 changed files with 399 additions and 52 deletions.
10 changes: 5 additions & 5 deletions fastlane/lib/fastlane/actions/docs/build_app.md
Expand Up @@ -74,7 +74,7 @@ That's all you need to build your application. If you want more control, here ar
fastlane gym --workspace "Example.xcworkspace" --scheme "AppName" --clean
```

If you need to use a different Xcode installation, use `xcode-select` or define `DEVELOPER_DIR`:
If you need to use a different Xcode installation, use `[xcodes](https://docs.fastlane.tools/actions/xcodes)` or define `DEVELOPER_DIR`:

```no-highlight
DEVELOPER_DIR="/Applications/Xcode6.2.app" fastlane gym
Expand Down Expand Up @@ -150,7 +150,7 @@ build_app(
scheme: "Release",
export_method: "app-store",
export_options: {
provisioningProfiles: {
provisioningProfiles: {
"com.example.bundleid" => "Provisioning Profile Name",
"com.example.bundleid2" => "Provisioning Profile Name 2"
}
Expand Down Expand Up @@ -184,10 +184,10 @@ end
error do |lane, exception|
slack(
# message with short human friendly message
message: exception.to_s,
success: false,
message: exception.to_s,
success: false,
# Output containing extended log output
payload: { "Output" => exception.error_info.to_s }
payload: { "Output" => exception.error_info.to_s }
)
end
```
Expand Down
2 changes: 1 addition & 1 deletion fastlane/lib/fastlane/actions/docs/run_tests.md
Expand Up @@ -100,7 +100,7 @@ That's all you need to run your tests. If you want more control, here are some a
fastlane scan --workspace "Example.xcworkspace" --scheme "AppName" --device "iPhone 6" --clean
```

If you need to use a different Xcode install, use `xcode-select` or define `DEVELOPER_DIR`:
If you need to use a different Xcode install, use `[xcodes](https://docs.fastlane.tools/actions/xcodes)` or define `DEVELOPER_DIR`:

```no-highlight
DEVELOPER_DIR="/Applications/Xcode6.2.app" scan
Expand Down
6 changes: 5 additions & 1 deletion fastlane/lib/fastlane/actions/xcode_install.rb
Expand Up @@ -101,7 +101,11 @@ def self.example_code
end

def self.category
:building
:deprecated
end

def self.deprecated_notes
"The xcode-install gem, which this action depends on, has been sunset. Please migrate to [xcodes](https://docs.fastlane.tools/actions/xcodes). You can find a migration guide here: [xcpretty/xcode-install/MIGRATION.md](https://github.com/xcpretty/xcode-install/blob/master/MIGRATION.md)"
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion fastlane/lib/fastlane/actions/xcode_select.rb
Expand Up @@ -40,7 +40,7 @@ def self.description
def self.details
[
"Select and build with the Xcode installed at the provided path.",
"Use the `xcversion` action if you want to select an Xcode:",
"Use the `xcodes` action if you want to select an Xcode:",
"- Based on a version specifier or",
"- You don't have known, stable paths, as may happen in a CI environment."
].join("\n")
Expand Down
137 changes: 137 additions & 0 deletions fastlane/lib/fastlane/actions/xcodes.rb
@@ -0,0 +1,137 @@
module Fastlane
module Actions
module SharedValues
XCODES_XCODE_PATH = :XCODES_XCODE_PATH
end

class XcodesAction < Action
def self.run(params)
binary = params[:binary_path]
xcodes_raw_version = Actions.sh("#{binary} version", log: false)
xcodes_version = Gem::Version.new(xcodes_raw_version)
UI.message("Running xcodes version #{xcodes_version}")
if xcodes_version < Gem::Version.new("1.1.0")
UI.user_error!([
"xcodes action requires the minimum version of xcodes binary to be v1.1.0.",
"Please update xcodes. If you installed it via Homebrew, this can be done via 'brew upgrade xcodes'"
].join(" "))
end

version = params[:version]
command = []
command << binary

if (xcodes_args = params[:xcodes_args])
command << xcodes_args
Actions.sh(command.join(" "))
elsif !params[:select_for_current_build_only]
command << "install"
command << "'#{version}'"
command << "--update" if params[:update_list]
command << "--select"
Actions.sh(command.join(" "))
end

command = []
command << binary
command << "installed"
command << "'#{version}'"
# Prints something like /Applications/Xcode-14.app
xcode_path = Actions.sh(command.join(" ")).strip
xcode_developer_path = File.join(xcode_path, "/Contents/Developer")

UI.message("Setting Xcode version '#{version}' at '#{xcode_path}' for all build steps")
ENV["DEVELOPER_DIR"] = xcode_developer_path
Actions.lane_context[SharedValues::XCODES_XCODE_PATH] = xcode_developer_path
return xcode_path
end

#####################################################
# @!group Documentation
#####################################################

def self.description
"Make sure a certain version of Xcode is installed, installing it only if needed"
end

def self.details
[
"Makes sure a specific version of Xcode is installed. If that's not the case, it will automatically be downloaded by [xcodes](https://github.com/RobotsAndPencils/xcodes).",
"This will make sure to use the correct Xcode version for later actions.",
"Note that this action depends on [xcodes](https://github.com/RobotsAndPencils/xcodes) CLI, so make sure you have it installed in your environment. For the installation guide, see: https://github.com/RobotsAndPencils/xcodes#installation"
].join("\n")
end

def self.available_options
[
FastlaneCore::ConfigItem.new(key: :version,
env_name: "FL_XCODE_VERSION",
description: "The version number of the version of Xcode to install. Defaults to the value specified in the .xcode-version file",
default_value: Helper::XcodesHelper.read_xcode_version_file,
default_value_dynamic: true,
verify_block: Helper::XcodesHelper::Verify.method(:requirement)),
FastlaneCore::ConfigItem.new(key: :update_list,
env_name: "FL_XCODES_UPDATE_LIST",
description: "Whether the list of available Xcode versions should be updated before running the install command",
type: Boolean,
default_value: true),
FastlaneCore::ConfigItem.new(key: :select_for_current_build_only,
env_name: "FL_XCODES_SELECT_FOR_CURRENT_BUILD_ONLY",
description: [
"When true, it won't attempt to install an Xcode version, just find the installed Xcode version that best matches the passed version argument, and select it for the current build steps.",
"It doesn't change the global Xcode version (e.g. via 'xcrun xcode-select'), which would require sudo permissions — when this option is true, this action doesn't require sudo permissions"
].join(" "),
type: Boolean,
default_value: false),
FastlaneCore::ConfigItem.new(key: :binary_path,
env_name: "FL_XCODES_BINARY_PATH",
description: "Where the xcodes binary lives on your system (full path)",
default_value: Helper::XcodesHelper.find_xcodes_binary_path,
default_value_dynamic: true,
verify_block: proc do |value|
UI.user_error!("'xcodes' doesn't seem to be installed. Please follow the installation guide at https://github.com/RobotsAndPencils/xcodes#installation before proceeding") if value.empty?
UI.user_error!("Couldn't find xcodes binary at path '#{value}'") unless File.exist?(value)
end),
FastlaneCore::ConfigItem.new(key: :xcodes_args,
env_name: "FL_XCODES_ARGS",
description: "Pass in xcodes command line arguments directly. When present, other parameters are ignored and only this parameter is used to build the command to be executed",
type: :shell_string,
optional: true)
]
end

def self.output
[
['XCODES_XCODE_PATH', 'The path to the newly installed Xcode version']
]
end

def self.return_value
"The path to the newly installed Xcode version"
end

def self.return_type
:string
end

def self.authors
["rogerluan"]
end

def self.is_supported?(platform)
[:ios, :mac].include?(platform)
end

def self.example_code
[
'xcodes(version: "14.1")',
'xcodes # When missing, the version value defaults to the value specified in the .xcode-version file'
]
end

def self.category
:building
end
end
end
end
25 changes: 10 additions & 15 deletions fastlane/lib/fastlane/actions/xcversion.rb
Expand Up @@ -15,16 +15,6 @@ def self.run(params)
ENV["DEVELOPER_DIR"] = File.join(xcode.path, "/Contents/Developer")
end

def self.read_xcode_version_file
xcode_version_paths = Dir.glob(".xcode-version")

if xcode_version_paths.first
return File.read(xcode_version_paths.first).strip
end

return nil
end

def self.description
"Select an Xcode to use by version specifier"
end
Expand All @@ -44,10 +34,10 @@ def self.available_options
[
FastlaneCore::ConfigItem.new(key: :version,
env_name: "FL_XCODE_VERSION",
description: "The version of Xcode to select specified as a Gem::Version requirement string (e.g. '~> 7.1.0')",
default_value: self.read_xcode_version_file,
description: "The version of Xcode to select specified as a Gem::Version requirement string (e.g. '~> 7.1.0'). Defaults to the value specified in the .xcode-version file ",
default_value: Helper::XcodesHelper.read_xcode_version_file,
default_value_dynamic: true,
verify_block: Helper::XcversionHelper::Verify.method(:requirement))
verify_block: Helper::XcodesHelper::Verify.method(:requirement))
]
end

Expand All @@ -58,12 +48,17 @@ def self.is_supported?(platform)
def self.example_code
[
'xcversion(version: "8.1") # Selects Xcode 8.1.0',
'xcversion(version: "~> 8.1.0") # Selects the latest installed version from the 8.1.x set'
'xcversion(version: "~> 8.1.0") # Selects the latest installed version from the 8.1.x set',
'xcversion # When missing, the version value defaults to the value specified in the .xcode-version file'
]
end

def self.category
:building
:deprecated
end

def self.deprecated_notes
"The xcode-install gem, which this action depends on, has been sunset. Please migrate to [xcodes](https://docs.fastlane.tools/actions/xcodes). You can find a migration guide here: [xcpretty/xcode-install/MIGRATION.md](https://github.com/xcpretty/xcode-install/blob/master/MIGRATION.md)"
end
end
end
Expand Down
28 changes: 28 additions & 0 deletions fastlane/lib/fastlane/helper/xcodes_helper.rb
@@ -0,0 +1,28 @@
module Fastlane
module Helper
class XcodesHelper
def self.read_xcode_version_file
xcode_version_paths = Dir.glob(".xcode-version")

if xcode_version_paths.first
return File.read(xcode_version_paths.first).strip
end

return nil
end

def self.find_xcodes_binary_path
`which xcodes`.strip
end

module Verify
def self.requirement(req)
UI.user_error!("Version must be specified") if req.nil? || req.to_s.strip.size == 0
Gem::Requirement.new(req.to_s)
rescue Gem::Requirement::BadRequirementError
UI.user_error!("The requirement '#{req}' is not a valid RubyGems style requirement")
end
end
end
end
end
9 changes: 0 additions & 9 deletions fastlane/lib/fastlane/helper/xcversion_helper.rb
Expand Up @@ -11,15 +11,6 @@ def self.find_xcode(req)
req.satisfied_by?(Gem::Version.new(xcode.version))
end
end

module Verify
def self.requirement(req)
UI.user_error!("Version must be specified") if req.nil? || req.to_s.strip.size == 0
Gem::Requirement.new(req.to_s)
rescue Gem::Requirement::BadRequirementError
UI.user_error!("The requirement '#{req}' is not a valid RubyGems style requirement")
end
end
end
end
end

0 comments on commit 4b30089

Please sign in to comment.