diff --git a/lib/fastlane/plugin/create_simulator_devices/actions/create_simulator_devices_action.rb b/lib/fastlane/plugin/create_simulator_devices/actions/create_simulator_devices_action.rb index 76b3b97..f26cba1 100644 --- a/lib/fastlane/plugin/create_simulator_devices/actions/create_simulator_devices_action.rb +++ b/lib/fastlane/plugin/create_simulator_devices/actions/create_simulator_devices_action.rb @@ -22,7 +22,7 @@ def self.run(params) required_devices = params[:devices] UI.user_error!('No devices specified') if required_devices.nil? || required_devices.empty? - shell_helper = CreateSimulatorDevices::ShellHelper.new(print_command: params[:print_command], print_command_output: params[:print_command_output], action_context: self) + shell_helper = CreateSimulatorDevices::ShellHelper.new(verbose: verbose, print_command: params[:print_command], print_command_output: params[:print_command_output], action_context: self) runtime_helper = CreateSimulatorDevices::RuntimeHelper.new(cache_dir: params[:cache_dir], shell_helper: shell_helper, verbose: verbose) runner = CreateSimulatorDevices::Runner.new( diff --git a/lib/fastlane/plugin/create_simulator_devices/actions/gather_simctl_diagnose_action.rb b/lib/fastlane/plugin/create_simulator_devices/actions/gather_simctl_diagnose_action.rb index 9873d57..b1971cb 100644 --- a/lib/fastlane/plugin/create_simulator_devices/actions/gather_simctl_diagnose_action.rb +++ b/lib/fastlane/plugin/create_simulator_devices/actions/gather_simctl_diagnose_action.rb @@ -24,7 +24,7 @@ def self.run(params) required_devices = params[:devices] UI.user_error!('No devices specified') if required_devices.nil? || required_devices.empty? - shell_helper = CreateSimulatorDevices::ShellHelper.new(print_command: params[:print_command], print_command_output: params[:print_command_output], action_context: self) + shell_helper = CreateSimulatorDevices::ShellHelper.new(verbose: verbose, print_command: params[:print_command], print_command_output: params[:print_command_output], action_context: self) runtime_helper = CreateSimulatorDevices::RuntimeHelper.new(cache_dir: nil, shell_helper: shell_helper, verbose: verbose) create_simulator_devices_runner = ::Fastlane::CreateSimulatorDevices::Runner.new( diff --git a/lib/fastlane/plugin/create_simulator_devices/helpers/create_simulator_devices/models/apple_build_version.rb b/lib/fastlane/plugin/create_simulator_devices/helpers/create_simulator_devices/models/apple_build_version.rb index 6f831fe..a9dcbee 100644 --- a/lib/fastlane/plugin/create_simulator_devices/helpers/create_simulator_devices/models/apple_build_version.rb +++ b/lib/fastlane/plugin/create_simulator_devices/helpers/create_simulator_devices/models/apple_build_version.rb @@ -62,15 +62,15 @@ def almost_equal?(other) end def major - @build_version.match(/^([0-9]+)([A-Z][0-9]{2,3})([0-9]+)/)[1] + @build_version.match(/^([0-9]+)([A-Z][0-9]{1,3})([0-9]+)/)[1] end def minor - @build_version.match(/^([0-9]+)([A-Z][0-9]{2,3})([0-9]+)/)[2] + @build_version.match(/^([0-9]+)([A-Z][0-9]{1,3})([0-9]+)/)[2] end def patch - @build_version.match(/^([0-9]+)([A-Z][0-9]{2,3})([0-9]+)/)[3] + @build_version.match(/^([0-9]+)([A-Z][0-9]{1,3})([0-9]+)/)[3] end def <(other) diff --git a/lib/fastlane/plugin/create_simulator_devices/helpers/create_simulator_devices/shell_helper.rb b/lib/fastlane/plugin/create_simulator_devices/helpers/create_simulator_devices/shell_helper.rb index ed82508..f84515f 100644 --- a/lib/fastlane/plugin/create_simulator_devices/helpers/create_simulator_devices/shell_helper.rb +++ b/lib/fastlane/plugin/create_simulator_devices/helpers/create_simulator_devices/shell_helper.rb @@ -11,9 +11,10 @@ class ShellHelper UI = ::Fastlane::UI unless defined?(UI) # Proprty verbose - attr_accessor :print_command, :print_command_output, :action_context + attr_accessor :verbose, :print_command, :print_command_output, :action_context - def initialize(print_command: false, print_command_output: false, action_context: nil) + def initialize(verbose: false, print_command: false, print_command_output: false, action_context: nil) + self.verbose = verbose self.print_command = print_command self.print_command_output = print_command_output self.action_context = action_context @@ -141,8 +142,6 @@ def delete_runtime(runtime_identifier) end def download_runtime(missing_runtime, cache_dir) - UI.message("Downloading #{missing_runtime.runtime_name} to #{cache_dir}. This may take a while...") - command = [ 'xcrun', 'xcodebuild', @@ -162,6 +161,30 @@ def download_runtime(missing_runtime, cache_dir) command << missing_runtime.product_version.to_s.shellescape end + simulator_architecture_variant = 'universal' + + if Fastlane::Helper.xcode_at_least?('26') && missing_runtime.product_version >= '26' + xcode_binary_path = File.join(Fastlane::Helper.xcode_path, '..', 'MacOS', 'Xcode') + xcode_binary_path = File.expand_path(xcode_binary_path) + + UI.message('Getting Xcode architecture variants with lipo...') if verbose + + xcode_architecture_variants = sh(command: "lipo -archs #{xcode_binary_path.shellescape}", print_command: print_command, print_command_output: print_command_output).split + + UI.message("Xcode architecture variants: #{xcode_architecture_variants}") if verbose + + simulator_architecture_variant = if xcode_architecture_variants.include?('x86_64') + 'universal' + else + 'arm64' + end + + command << '-architectureVariant' + command << simulator_architecture_variant.shellescape + end + + UI.message("Downloading #{missing_runtime.runtime_name} (arch: #{simulator_architecture_variant}) to #{cache_dir}. This may take a while...") + sh(command: command.join(' '), print_command: true, print_command_output: true) end diff --git a/spec/create_simulator_devices/shell_helper_spec.rb b/spec/create_simulator_devices/shell_helper_spec.rb index 0275769..4f77029 100644 --- a/spec/create_simulator_devices/shell_helper_spec.rb +++ b/spec/create_simulator_devices/shell_helper_spec.rb @@ -208,12 +208,14 @@ end describe '#download_runtime' do - it 'downloads runtime platform using xcodebuild' do + it 'downloads runtime platform using xcodebuild (< 26)' do # GIVEN: Runtime platform and version details missing_platform = 'iOS' missing_version = '17.4' cache_dir = '/tmp/test_cache' + allow(Fastlane::Helper).to receive(:xcode_at_least?).with('26').and_return(false) + missing_runtime = RequiredRuntime.new(sdk_platform: nil, os_name: missing_platform, product_version: missing_version, product_build_version: AppleBuildVersion.new('21E210'), is_latest: false) expect(sut).to receive(:sh).with( command: "xcrun xcodebuild -verbose -exportPath #{cache_dir.shellescape} -downloadPlatform #{missing_platform.shellescape} -buildVersion #{missing_version.shellescape}", @@ -226,6 +228,64 @@ # THEN: Should execute the xcodebuild download command (expectation verified above) end + + it 'downloads runtime platform using xcodebuild (>= 26, universal)' do + # GIVEN: Runtime platform and version details + missing_platform = 'iOS' + missing_version = '26.1' + cache_dir = '/tmp/test_cache' + + allow(Fastlane::Helper).to receive(:xcode_at_least?).with('26').and_return(true) + allow(Fastlane::Helper).to receive(:xcode_version).and_return('26.1') + allow(Fastlane::Helper).to receive(:xcode_path).and_return('/Applications/Xcode.app/Contents/Developer') + + allow(sut).to receive(:sh).with( + command: 'lipo -archs /Applications/Xcode.app/Contents/MacOS/Xcode', + print_command: false, + print_command_output: false + ).and_return('x86_64 arm64') + + missing_runtime = RequiredRuntime.new(sdk_platform: nil, os_name: missing_platform, product_version: missing_version, product_build_version: AppleBuildVersion.new('23B77'), is_latest: false) + expect(sut).to receive(:sh).with( + command: "xcrun xcodebuild -verbose -exportPath #{cache_dir.shellescape} -downloadPlatform #{missing_platform.shellescape} -buildVersion #{missing_version.shellescape} -architectureVariant universal", + print_command: true, + print_command_output: true + ) + + # WHEN: Downloading runtime + sut.download_runtime(missing_runtime, cache_dir) + + # THEN: Should execute the xcodebuild download command (expectation verified above) + end + + it 'downloads runtime platform using xcodebuild (>= 26, arm64)' do + # GIVEN: Runtime platform and version details + missing_platform = 'iOS' + missing_version = '26.1' + cache_dir = '/tmp/test_cache' + + allow(Fastlane::Helper).to receive(:xcode_at_least?).with('26').and_return(true) + allow(Fastlane::Helper).to receive(:xcode_version).and_return('26.1') + allow(Fastlane::Helper).to receive(:xcode_path).and_return('/Applications/Xcode.app/Contents/Developer') + + allow(sut).to receive(:sh).with( + command: 'lipo -archs /Applications/Xcode.app/Contents/MacOS/Xcode', + print_command: false, + print_command_output: false + ).and_return('arm64') + + missing_runtime = RequiredRuntime.new(sdk_platform: nil, os_name: missing_platform, product_version: missing_version, product_build_version: AppleBuildVersion.new('23B77'), is_latest: false) + expect(sut).to receive(:sh).with( + command: "xcrun xcodebuild -verbose -exportPath #{cache_dir.shellescape} -downloadPlatform #{missing_platform.shellescape} -buildVersion #{missing_version.shellescape} -architectureVariant arm64", + print_command: true, + print_command_output: true + ) + + # WHEN: Downloading runtime + sut.download_runtime(missing_runtime, cache_dir) + + # THEN: Should execute the xcodebuild download command (expectation verified above) + end end describe '#import_runtime' do