fastlane-plugin-create_simulator_devices 0.0.12 → 0.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (16) hide show
  1. checksums.yaml +4 -4
  2. data/lib/fastlane/plugin/create_simulator_devices/actions/create_simulator_devices_action.rb +37 -4
  3. data/lib/fastlane/plugin/create_simulator_devices/actions/gather_simctl_diagnose_action.rb +153 -0
  4. data/lib/fastlane/plugin/create_simulator_devices/helpers/create_simulator_devices/models/apple_build_version.rb +11 -3
  5. data/lib/fastlane/plugin/create_simulator_devices/helpers/create_simulator_devices/models/device_naming_style.rb +14 -0
  6. data/lib/fastlane/plugin/create_simulator_devices/helpers/create_simulator_devices/models.rb +1 -0
  7. data/lib/fastlane/plugin/create_simulator_devices/helpers/create_simulator_devices/runner.rb +92 -10
  8. data/lib/fastlane/plugin/create_simulator_devices/helpers/create_simulator_devices/runtime_helper.rb +10 -3
  9. data/lib/fastlane/plugin/create_simulator_devices/helpers/create_simulator_devices/shell_helper.rb +12 -3
  10. data/lib/fastlane/plugin/create_simulator_devices/helpers/gather_simctl_diagnose/module.rb +10 -0
  11. data/lib/fastlane/plugin/create_simulator_devices/helpers/gather_simctl_diagnose/runner.rb +84 -0
  12. data/lib/fastlane/plugin/create_simulator_devices/helpers/gather_simctl_diagnose/scripts/collect_simctl_diagnose_data.expect +9 -0
  13. data/lib/fastlane/plugin/create_simulator_devices/helpers/gather_simctl_diagnose/scripts/collect_simctl_diagnose_data.sh +40 -0
  14. data/lib/fastlane/plugin/create_simulator_devices/helpers/gather_simctl_diagnose/shared_values.rb +11 -0
  15. data/lib/fastlane/plugin/create_simulator_devices/version.rb +1 -1
  16. metadata +9 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0e4f2a4d905fad425dc7b871cb55ee0ea48c0d36b8417d26d6cfa1a49fd97737
4
- data.tar.gz: 1f123387acdf4df42f374ec7ba14d2c0aed1f30587c1c2ba8c9f362a848641fe
3
+ metadata.gz: d67f3fc97805357d10286a81d1d7fcbf18ede6af69da32b176c6fcd73ab72a2f
4
+ data.tar.gz: baf9ecd15c0dc7fbc66c413f61d48f44bf37a6ff32963fb4647cdcf9b586bdeb
5
5
  SHA512:
6
- metadata.gz: 7183aeb1d262c2b7faeadb35750ecf0f04982ef9a978b2e397ec66c5fac5a5a10b94494a525355b74711a25a26a8f7c5a1af2415c57fb6d2db2ff7776461dd16
7
- data.tar.gz: 9404401a0f27c170011f87e3707a901f0ac3ae1294ce7ebceb1519648b94b3e6e74cd049b520d288471160852d0c1cf1bc931a05ca8413fa177624da82b60cf3
6
+ metadata.gz: 8de8e706a371d863ee1646ca85c899ee96c42ce7f94a2c6bb97fabbc579b03af429ddc439356ea7a6d429507e0ccc4c21fda66007ffef6a16fd6925e99ef033f
7
+ data.tar.gz: ddd40fb588c98656bae804668cc5ee899af6c09fc660a629edc6a6c443f431832375a0e3930dcb27ebc89153d325780386961dc8e4fececb877fb16b2488648a
@@ -4,6 +4,7 @@ require 'fastlane'
4
4
  require 'spaceship'
5
5
  require_relative '../helpers/create_simulator_devices/runner'
6
6
  require_relative '../helpers/create_simulator_devices/models'
7
+ require_relative '../helpers/create_simulator_devices/models/device_naming_style'
7
8
 
8
9
  module Fastlane
9
10
  module Actions
@@ -22,12 +23,16 @@ module Fastlane
22
23
  UI.user_error!('No devices specified') if required_devices.nil? || required_devices.empty?
23
24
 
24
25
  shell_helper = CreateSimulatorDevices::ShellHelper.new(print_command: params[:print_command], print_command_output: params[:print_command_output], action_context: self)
25
- runtime_helper = CreateSimulatorDevices::RuntimeHelper.new(cache_dir: params[:cache_dir], shell_helper:, verbose:)
26
+ runtime_helper = CreateSimulatorDevices::RuntimeHelper.new(cache_dir: params[:cache_dir], shell_helper: shell_helper, verbose: verbose)
26
27
 
27
28
  runner = CreateSimulatorDevices::Runner.new(
28
29
  runtime_helper: runtime_helper,
29
30
  shell_helper: shell_helper,
30
- verbose: verbose
31
+ verbose: verbose,
32
+ can_rename_devices: params[:rename_devices],
33
+ can_delete_duplicate_devices: params[:delete_duplicate_devices],
34
+ device_naming_style: params[:device_naming_style].to_sym,
35
+ remove_cached_runtimes: params[:remove_cached_runtimes]
31
36
  )
32
37
 
33
38
  runner.run(required_devices)
@@ -52,7 +57,7 @@ module Fastlane
52
57
  )"
53
58
  end
54
59
 
55
- def self.available_options
60
+ def self.available_options # rubocop:disable Metrics/MethodLength
56
61
  [
57
62
  ::FastlaneCore::ConfigItem.new(key: :devices,
58
63
  env_name: 'SCAN_DEVICES',
@@ -80,7 +85,35 @@ module Fastlane
80
85
  description: 'Print xcrun simctl commands output',
81
86
  type: Boolean,
82
87
  optional: true,
83
- default_value: false)
88
+ default_value: false),
89
+ ::FastlaneCore::ConfigItem.new(key: :rename_devices,
90
+ env_name: 'CREATE_SIMULATOR_DEVICES_RENAME_DEVICES',
91
+ description: 'Rename devices if needed',
92
+ type: Boolean,
93
+ optional: true,
94
+ default_value: false),
95
+ ::FastlaneCore::ConfigItem.new(key: :delete_duplicate_devices,
96
+ env_name: 'CREATE_SIMULATOR_DEVICES_DELETE_DUPLICATE_DEVICES',
97
+ description: 'Delete duplicate devices',
98
+ type: Boolean,
99
+ optional: true,
100
+ default_value: false),
101
+ ::FastlaneCore::ConfigItem.new(key: :device_naming_style,
102
+ env_name: 'CREATE_SIMULATOR_DEVICES_DEVICE_NAMING_STYLE',
103
+ description: 'Device naming style',
104
+ type: String,
105
+ optional: true,
106
+ default_value: CreateSimulatorDevices::DeviceNamingStyle::SCAN.to_s,
107
+ verify_block: proc do |value|
108
+ allowed_values = CreateSimulatorDevices::DeviceNamingStyle::ALL
109
+ UI.user_error!("Invalid device naming style: #{value}. Allowed values: #{allowed_values.map(&:to_s).join(', ')}") unless allowed_values.include?(value.to_sym)
110
+ end),
111
+ ::FastlaneCore::ConfigItem.new(key: :remove_cached_runtimes,
112
+ env_name: 'CREATE_SIMULATOR_DEVICES_REMOVE_CACHED_RUNTIMES',
113
+ description: 'Remove cached runtimes after successful installation',
114
+ type: Boolean,
115
+ optional: true,
116
+ default_value: true)
84
117
  ]
85
118
  end
86
119
 
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fastlane'
4
+ require 'spaceship'
5
+ require_relative '../helpers/create_simulator_devices/shell_helper'
6
+ require_relative '../helpers/create_simulator_devices/runner'
7
+ require_relative '../helpers/create_simulator_devices/models/device_naming_style'
8
+ require_relative '../helpers/gather_simctl_diagnose/runner'
9
+
10
+ module Fastlane
11
+ module Actions
12
+ GatherSimctlDiagnose = ::Fastlane::GatherSimctlDiagnose
13
+ CreateSimulatorDevices = ::Fastlane::CreateSimulatorDevices
14
+
15
+ # Gather simctl diagnose data.
16
+ class GatherSimctlDiagnoseAction < Fastlane::Action
17
+ UI = ::Fastlane::UI unless defined?(UI)
18
+
19
+ attr_accessor :shell_helper
20
+
21
+ def self.run(params)
22
+ verbose = params[:verbose]
23
+ params[:devices] = params[:devices].split(',').map(&:strip) if params[:devices].is_a?(String)
24
+ required_devices = params[:devices]
25
+ UI.user_error!('No devices specified') if required_devices.nil? || required_devices.empty?
26
+
27
+ shell_helper = CreateSimulatorDevices::ShellHelper.new(print_command: params[:print_command], print_command_output: params[:print_command_output], action_context: self)
28
+ runtime_helper = CreateSimulatorDevices::RuntimeHelper.new(cache_dir: nil, shell_helper: shell_helper, verbose: verbose)
29
+
30
+ create_simulator_devices_runner = ::Fastlane::CreateSimulatorDevices::Runner.new(
31
+ runtime_helper: runtime_helper,
32
+ shell_helper: shell_helper,
33
+ verbose: verbose,
34
+ can_rename_devices: false,
35
+ can_delete_duplicate_devices: false,
36
+ device_naming_style: params[:device_naming_style].to_sym,
37
+ remove_cached_runtimes: false
38
+ )
39
+
40
+ runner = GatherSimctlDiagnose::Runner.new(
41
+ runtime_helper: runtime_helper,
42
+ shell_helper: shell_helper,
43
+ verbose: verbose,
44
+ device_naming_style: params[:device_naming_style].to_sym,
45
+ create_simulator_devices_runner: create_simulator_devices_runner,
46
+ output_dir: params[:output_dir],
47
+ timeout: params[:timeout],
48
+ include_all_device_logs: params[:include_all_device_logs],
49
+ include_booted_device_data_directory: params[:include_booted_device_data_directory]
50
+ )
51
+
52
+ runner.run(required_devices)
53
+ end
54
+
55
+ #####################################################
56
+ # @!group Documentation
57
+ #####################################################
58
+
59
+ def self.description
60
+ 'Gathers simctl diagnose data'
61
+ end
62
+
63
+ def self.details
64
+ "This action gathers simctl diagnose data.
65
+
66
+ Usage sample:
67
+
68
+ gather_simctl_diagnose(
69
+ devices: ['iPhone 15 (17.0)', 'iPhone 16', 'iPhone 14 (16.3)']
70
+ output_dir: 'diagnose'
71
+ )"
72
+ end
73
+
74
+ def self.available_options # rubocop:disable Metrics/MethodLength
75
+ [
76
+ ::FastlaneCore::ConfigItem.new(key: :devices,
77
+ env_name: 'SCAN_DEVICES',
78
+ description: 'A list of simulator devices to install (e.g. "iPhone 16")',
79
+ is_string: false,
80
+ default_value: 'iPhone 16'),
81
+ ::FastlaneCore::ConfigItem.new(key: :verbose,
82
+ env_name: 'VERBOSE',
83
+ description: 'Verbose output',
84
+ type: Boolean,
85
+ optional: true,
86
+ default_value: ::FastlaneCore::Globals.verbose?,
87
+ default_value_dynamic: true),
88
+ ::FastlaneCore::ConfigItem.new(key: :output_dir,
89
+ env_name: 'GATHER_SIMCTL_DIAGNOSE_OUTPUT_DIR',
90
+ description: 'Output directory',
91
+ type: String,
92
+ optional: false),
93
+ ::FastlaneCore::ConfigItem.new(key: :print_command,
94
+ description: 'Print xcrun simctl commands',
95
+ type: Boolean,
96
+ optional: true,
97
+ default_value: false),
98
+ ::FastlaneCore::ConfigItem.new(key: :print_command_output,
99
+ description: 'Print xcrun simctl commands output',
100
+ type: Boolean,
101
+ optional: true,
102
+ default_value: false),
103
+ ::FastlaneCore::ConfigItem.new(key: :device_naming_style,
104
+ env_name: 'CREATE_SIMULATOR_DEVICES_DEVICE_NAMING_STYLE',
105
+ description: 'Device naming style',
106
+ type: String,
107
+ optional: true,
108
+ default_value: CreateSimulatorDevices::DeviceNamingStyle::SCAN.to_s,
109
+ verify_block: proc do |value|
110
+ allowed_values = CreateSimulatorDevices::DeviceNamingStyle::ALL
111
+ UI.user_error!("Invalid device naming style: #{value}. Allowed values: #{allowed_values.map(&:to_s).join(', ')}") unless allowed_values.include?(value.to_sym)
112
+ end),
113
+ ::FastlaneCore::ConfigItem.new(key: :timeout,
114
+ env_name: 'GATHER_SIMCTL_DIAGNOSE_TIMEOUT',
115
+ description: 'Timeout',
116
+ type: Integer,
117
+ optional: true,
118
+ default_value: 300),
119
+ ::FastlaneCore::ConfigItem.new(key: :include_all_device_logs,
120
+ env_name: 'GATHER_SIMCTL_DIAGNOSE_INCLUDE_ALL_DEVICE_LOGS',
121
+ description: 'Include all device logs',
122
+ type: Boolean,
123
+ optional: true,
124
+ default_value: false),
125
+ ::FastlaneCore::ConfigItem.new(key: :include_booted_device_data_directory,
126
+ env_name: 'GATHER_SIMCTL_DIAGNOSE_INCLUDE_BOOTED_DEVICE_DATA_DIRECTORY',
127
+ description: 'Include booted device data directory. Warning: May include private information, app data containers, and increases the size of the archive! Default is NOT to collect the data container',
128
+ type: Boolean,
129
+ optional: true,
130
+ default_value: false)
131
+ ]
132
+ end
133
+
134
+ def self.output
135
+ [
136
+ ['GATHER_SIMCTL_DIAGNOSE_OUTPUT_FILE', 'Output archive file with simctl diagnose data']
137
+ ]
138
+ end
139
+
140
+ def self.return_value
141
+ 'Returns a path to the output archive file with simctl diagnose data'
142
+ end
143
+
144
+ def self.authors
145
+ ['nekrich']
146
+ end
147
+
148
+ def self.is_supported?(_platform) # rubocop:disable Naming/PredicatePrefix
149
+ true
150
+ end
151
+ end
152
+ end
153
+ end
@@ -58,11 +58,19 @@ module Fastlane
58
58
  return self == other unless lhs_is_beta && rhs_is_beta && @build_version.length == rhs_build_version.to_s.length
59
59
 
60
60
  # Take only leading chars up to the first letter included e.g. 22C146 -> 22C
61
- minor_version == rhs_build_version.minor_version
61
+ major == rhs_build_version.major && minor == rhs_build_version.minor && patch == rhs_build_version.patch
62
62
  end
63
63
 
64
- def minor_version
65
- AppleBuildVersion.new(@build_version.match(/^[0-9]+[A-Z]+/)[0])
64
+ def major
65
+ @build_version.match(/^([0-9]+)([A-Z][0-9]{2,3})([0-9]+)/)[1]
66
+ end
67
+
68
+ def minor
69
+ @build_version.match(/^([0-9]+)([A-Z][0-9]{2,3})([0-9]+)/)[2]
70
+ end
71
+
72
+ def patch
73
+ @build_version.match(/^([0-9]+)([A-Z][0-9]{2,3})([0-9]+)/)[3]
66
74
  end
67
75
 
68
76
  def <(other)
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fastlane
4
+ module CreateSimulatorDevices
5
+ class DeviceNamingStyle
6
+ SCAN = :scan
7
+ RUN_TESTS = :run_tests
8
+ SNAPSHOT = :snapshot
9
+ CAPTURE_IOS_SCREENSHOTS = :capture_ios_screenshots
10
+
11
+ ALL = [SCAN, RUN_TESTS, SNAPSHOT, CAPTURE_IOS_SCREENSHOTS].freeze
12
+ end
13
+ end
14
+ end
@@ -8,6 +8,7 @@ require_relative 'models/xcodebuild/sdk'
8
8
  require_relative 'models/required_device'
9
9
  require_relative 'models/required_runtime'
10
10
  require_relative 'models/apple_build_version'
11
+ require_relative 'models/device_naming_style'
11
12
  require 'fastlane'
12
13
 
13
14
  module Fastlane
@@ -3,20 +3,25 @@
3
3
  require_relative 'runtime_helper'
4
4
  require 'fastlane'
5
5
  require_relative 'shared_values'
6
+ require_relative 'models/device_naming_style'
6
7
 
7
8
  module Fastlane
8
9
  # Create simulator devices.
9
10
  module CreateSimulatorDevices
10
11
  # Does all the work to create simulator devices.
11
- class Runner
12
+ class Runner # rubocop:disable Metrics/ClassLength
12
13
  UI = ::Fastlane::UI unless defined?(UI)
13
14
 
14
- attr_accessor :shell_helper, :verbose, :runtime_helper
15
+ attr_accessor :shell_helper, :verbose, :runtime_helper, :can_rename_devices, :can_delete_duplicate_devices, :device_naming_style, :remove_cached_runtimes
15
16
 
16
- def initialize(runtime_helper:, shell_helper:, verbose:)
17
+ def initialize(runtime_helper:, shell_helper:, verbose:, can_rename_devices:, can_delete_duplicate_devices:, device_naming_style:, remove_cached_runtimes:) # rubocop:disable Metrics/ParameterLists
17
18
  self.shell_helper = shell_helper
18
19
  self.verbose = verbose
19
20
  self.runtime_helper = runtime_helper
21
+ self.can_rename_devices = can_rename_devices
22
+ self.can_delete_duplicate_devices = can_delete_duplicate_devices
23
+ self.device_naming_style = device_naming_style
24
+ self.remove_cached_runtimes = remove_cached_runtimes
20
25
  end
21
26
 
22
27
  def run(devices)
@@ -50,11 +55,11 @@ module Fastlane
50
55
 
51
56
  log_matched_devices(matched_devices: matched_devices)
52
57
 
53
- Actions.lane_context[Actions::SharedValues::AVAILABLE_SIMULATOR_DEVICES] = matched_devices
54
-
55
- matched_devices_names = matched_devices.map(&:description)
58
+ matched_devices_names = matched_devices.map { |matched_device| returning_device_name_for_required_device(matched_device) }
56
59
  UI.message("Available simulator devices: #{matched_devices_names.join(', ')}")
57
60
 
61
+ Actions.lane_context[Actions::SharedValues::AVAILABLE_SIMULATOR_DEVICES] = matched_devices_names
62
+
58
63
  matched_devices_names
59
64
  end
60
65
 
@@ -85,14 +90,91 @@ module Fastlane
85
90
 
86
91
  simctl_devices = shell_helper.simctl_devices_for_runtimes[required_device.simctl_runtime.identifier.to_sym]
87
92
 
88
- return [] if simctl_devices.nil?
93
+ return nil if simctl_devices.nil?
89
94
 
90
95
  # Find the device with the same name as the required device.
91
96
  devices_with_same_type = simctl_devices
92
97
  .select { |simctl_device| simctl_device.device_type_identifier == required_device.device_type.identifier }
93
98
 
94
- devices_with_same_type
95
- .detect { |simctl_device| simctl_device.name == required_device.device_type.name }
99
+ preferred_device_name = device_name_for_required_device(required_device)
100
+
101
+ # Prefer device with the same name that includes the runtime version.
102
+ matching_device = devices_with_same_type.detect { |simctl_device| simctl_device.name == preferred_device_name }
103
+
104
+ if can_rename_devices
105
+ # Otherwise, if rename is enabled, use the first device with the same type.
106
+ matching_device ||= devices_with_same_type.first
107
+ rename_device_if_needed(matching_device, preferred_device_name)
108
+ end
109
+
110
+ delete_duplicate_devices(devices_with_same_type, matching_device) if can_delete_duplicate_devices
111
+
112
+ matching_device
113
+ end
114
+
115
+ def rename_device_if_needed(matching_device, preferred_device_name)
116
+ return if matching_device.nil? || matching_device.name == preferred_device_name
117
+
118
+ UI.message("Renaming device #{matching_device.name} (udid: #{matching_device.udid}) to #{preferred_device_name}")
119
+ shell_helper.rename_device(udid: matching_device.udid, name: preferred_device_name)
120
+ matching_device.name = preferred_device_name
121
+ end
122
+
123
+ def delete_duplicate_devices(matching_devices, matching_device)
124
+ return if matching_device.nil?
125
+
126
+ matching_devices
127
+ .reject { |simctl_device| simctl_device.udid == matching_device.udid }
128
+ .each do |simctl_device|
129
+ UI.message("Deleting duplicate device #{simctl_device.name} (udid: #{simctl_device.udid})")
130
+ shell_helper.delete_device(udid: simctl_device.udid)
131
+ end
132
+ end
133
+
134
+ # Returns the device name for the required device.
135
+ #
136
+ # This name is used in the simctl device name.
137
+ def device_name_for_required_device(required_device)
138
+ case device_naming_style
139
+ when DeviceNamingStyle::SCAN, DeviceNamingStyle::RUN_TESTS
140
+ # scan modifies the device name by removing the runtime version when searching for a passed device name.
141
+ # E.g.:
142
+ # * given "iPhone 15 (17.0)" match will search for simulator named "iPhone 15" with the SDK version 17.0.
143
+ # * given "iPhone 15" match will search for simulator named "iPhone 15" with the default SDK version.
144
+ # So we need to name the device by the device type name for scan to find the correct device.
145
+ required_device.device_type.name
146
+ when DeviceNamingStyle::SNAPSHOT, DeviceNamingStyle::CAPTURE_IOS_SCREENSHOTS
147
+ # snapshot nither does not modify the device name when searching for a passed device name nor extracts the runtime version from the device name.
148
+ # E.g.:
149
+ # * given "iPhone 15 (17.0)" match will search for device named exactly "iPhone 15 (17.0)".
150
+ # * given "iPhone 15" match will search for device named exactly "iPhone 15".
151
+ # So we need to return the full device name for the required device for snapshot to find the correct device.
152
+ runtime_build = required_device.simctl_runtime.build_version
153
+ build_suffix = runtime_build.beta? ? "-#{runtime_build}" : ''
154
+ "#{required_device.device_type.name} (#{required_device.simctl_runtime.version}#{build_suffix})"
155
+ end
156
+ end
157
+
158
+ # Returns the device name for the required device.
159
+ #
160
+ # This name is used when passing the device name to the scan or snapshot.
161
+ def returning_device_name_for_required_device(required_device)
162
+ case device_naming_style
163
+ when DeviceNamingStyle::SCAN, DeviceNamingStyle::RUN_TESTS
164
+ # scan respects the runtime version in the devices list, so we need to return the full device name for the required device, otherwise the default SDK version will be used.
165
+ # E.g.:
166
+ # * given "iPhone 15 (17.0)" match will search for simulator named "iPhone 15" with the SDK version 17.0.
167
+ # * given "iPhone 15" match will search for simulator named "iPhone 15" with the default SDK version.
168
+ # So we need to return the device name and the required runtime version for scan to find the correct device.
169
+ "#{required_device.simctl_device.name} (#{required_device.required_runtime.product_version})"
170
+ when DeviceNamingStyle::SNAPSHOT, DeviceNamingStyle::CAPTURE_IOS_SCREENSHOTS
171
+ # snapshot does not modify the device name when searching for a passed device name nor extracts the runtime version from the device name.
172
+ # E.g.:
173
+ # * given "iPhone 15 (17.0)" match will search for device named exactly "iPhone 15 (17.0)".
174
+ # * given "iPhone 15" match will search for device named exactly "iPhone 15" .
175
+ # So we need to return the full device name for the required device for snapshot to find the correct device.
176
+ required_device.simctl_device.name
177
+ end
96
178
  end
97
179
 
98
180
  def create_missing_devices(required_devices)
@@ -109,7 +191,7 @@ module Fastlane
109
191
  UI.message('Creating missing devices')
110
192
  missing_devices.each do |missing_device|
111
193
  shell_helper.create_device(
112
- missing_device.device_type.name,
194
+ device_name_for_required_device(missing_device),
113
195
  missing_device.device_type.identifier,
114
196
  missing_device.simctl_runtime.identifier
115
197
  )
@@ -50,7 +50,7 @@ module Fastlane
50
50
  end
51
51
 
52
52
  missing_runtimes.each do |missing_runtime|
53
- download_and_install_missing_runtime(missing_runtime)
53
+ download_and_install_missing_runtime(missing_runtime, remove_cached_runtimes: remove_cached_runtimes)
54
54
  end
55
55
 
56
56
  # Update simctl_runtimes after installing the runtimes.
@@ -128,7 +128,7 @@ module Fastlane
128
128
  simctl_runtime
129
129
  end
130
130
 
131
- def download_and_install_missing_runtime(missing_runtime)
131
+ def download_and_install_missing_runtime(missing_runtime, remove_cached_runtimes:)
132
132
  UI.message("Attempting to install #{missing_runtime.runtime_name} runtime.")
133
133
 
134
134
  downloaded_runtime_file = cached_runtime_file(missing_runtime)
@@ -139,6 +139,8 @@ module Fastlane
139
139
  end
140
140
 
141
141
  shell_helper.import_runtime(downloaded_runtime_file, missing_runtime.runtime_name)
142
+
143
+ FileUtils.rm_rf(downloaded_runtime_file) if remove_cached_runtimes
142
144
  end
143
145
 
144
146
  def runtime_build_version_for_filename(filename)
@@ -161,7 +163,12 @@ module Fastlane
161
163
  # shipped with Xcode betas and use the same product version.
162
164
  # E.g. Xcode 26.0 Beta 3 has iOS 26.0 (23A5287e) SDK, but
163
165
  # xcodebuild downloads iphonesimulator_26.0_23A5287g.dmg as latest.
164
- runtime_dmg_search_pattern += missing_runtime.product_build_version.minor_version.to_s if missing_runtime.product_build_version
166
+ product_build_version = missing_runtime.product_build_version
167
+ if product_build_version
168
+ runtime_dmg_search_pattern += product_build_version.major.to_s
169
+ runtime_dmg_search_pattern += product_build_version.minor.to_s
170
+ runtime_dmg_search_pattern += product_build_version.patch.to_s
171
+ end
165
172
  runtime_dmg_search_pattern += '*.dmg'
166
173
 
167
174
  if verbose
@@ -66,7 +66,7 @@ module Fastlane
66
66
  @simctl_device_types
67
67
  end
68
68
 
69
- def delete_device(udid)
69
+ def delete_device(udid:)
70
70
  UI.message("Deleting device #{udid}...")
71
71
  sh(command: "xcrun simctl delete #{udid.shellescape}")
72
72
  end
@@ -130,6 +130,11 @@ module Fastlane
130
130
  sh(command: "xcrun simctl create #{name.shellescape} #{device_type_identifier.shellescape} #{runtime_identifier.shellescape}")
131
131
  end
132
132
 
133
+ def rename_device(udid:, name:)
134
+ UI.message("Renaming device with udid #{udid} to #{name}")
135
+ sh(command: "xcrun simctl rename #{udid.shellescape} #{name.shellescape}")
136
+ end
137
+
133
138
  def delete_runtime(runtime_identifier)
134
139
  UI.message("Deleting runtime #{runtime_identifier}...")
135
140
  sh(command: "xcrun simctl runtime delete #{runtime_identifier.shellescape}")
@@ -150,8 +155,12 @@ module Fastlane
150
155
  missing_runtime.os_name.shellescape
151
156
  ]
152
157
 
153
- command << '-buildVersion'
154
- command << missing_runtime.product_version.to_s.shellescape
158
+ is_beta = missing_runtime.product_build_version.nil? ? false : missing_runtime.product_build_version.beta?
159
+
160
+ unless is_beta
161
+ command << '-buildVersion'
162
+ command << missing_runtime.product_version.to_s.shellescape
163
+ end
155
164
 
156
165
  sh(command: command.join(' '), print_command: true, print_command_output: true)
157
166
  end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fastlane'
4
+
5
+ module Fastlane
6
+ # Gather simctl diagnose data.
7
+ module GatherSimctlDiagnose
8
+ UI = ::Fastlane::UI unless defined?(UI)
9
+ end
10
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'module'
4
+ require 'fastlane'
5
+ require_relative 'shared_values'
6
+ require_relative '../create_simulator_devices/models/device_naming_style'
7
+
8
+ module Fastlane
9
+ # Gather simctl diagnose data.
10
+ module GatherSimctlDiagnose
11
+ # Does all the work to create simulator devices.
12
+ class Runner
13
+ UI = ::Fastlane::UI unless defined?(UI)
14
+
15
+ attr_accessor :shell_helper, :verbose, :runtime_helper, :device_naming_style, :create_simulator_devices_runner, :output_dir, :timeout, :include_all_device_logs, :include_booted_device_data_directory, :print_command, :print_command_output
16
+
17
+ def initialize( # rubocop:disable Metrics/ParameterLists
18
+ runtime_helper:,
19
+ shell_helper:,
20
+ verbose:,
21
+ device_naming_style:,
22
+ create_simulator_devices_runner:,
23
+ output_dir:,
24
+ timeout:,
25
+ include_all_device_logs:,
26
+ include_booted_device_data_directory:
27
+ )
28
+ self.shell_helper = shell_helper
29
+ self.verbose = verbose
30
+ self.runtime_helper = runtime_helper
31
+ self.device_naming_style = device_naming_style
32
+ self.create_simulator_devices_runner = create_simulator_devices_runner
33
+ self.output_dir = output_dir
34
+ self.timeout = timeout
35
+ self.include_all_device_logs = include_all_device_logs
36
+ self.include_booted_device_data_directory = include_booted_device_data_directory
37
+ end
38
+
39
+ def run(devices) # rubocop:disable Metrics/AbcSize
40
+ UI.message("Simulator devices to gather diagnose data: #{devices.join(', ')}")
41
+
42
+ # Create distict required devices from a given list of device strings.
43
+ required_devices = devices
44
+ .filter_map { |device| create_simulator_devices_runner.required_device_for_device(device) }
45
+ .uniq { |required_device| [required_device.device_type.name, required_device.required_runtime.product_version] }
46
+
47
+ if verbose
48
+ UI.message('Unique required devices:')
49
+ UI.message(" #{required_devices.map(&:description).join("\n ")}")
50
+ end
51
+
52
+ # Return distinct matched devices strings
53
+ matched_devices = create_simulator_devices_runner.find_runtime_and_device_for_required_devices(required_devices)
54
+ .reject { |required_device| required_device.simctl_device.nil? }
55
+
56
+ matched_devices_udids = matched_devices.map { |matched_device| matched_device.simctl_device.udid }
57
+ UI.message("Available simulator devices: #{matched_devices_udids.join(', ')}")
58
+
59
+ temp_output_dir = "#{File.expand_path(output_dir)}/simctl_diagnose_#{Time.now.strftime('%Y%m%d%H%M%S')}"
60
+
61
+ diagnose_args = matched_devices_udids.map { |udid| "--udid=#{udid}" }
62
+ diagnose_args << '-b' # Do NOT show the resulting archive in a Finder window upon completion.
63
+ diagnose_args << '--all-logs' if include_all_device_logs
64
+ diagnose_args << '--data-container' if include_booted_device_data_directory
65
+ diagnose_args << "--timeout=#{timeout}"
66
+
67
+ cmd_args = diagnose_args.map(&:shellescape).join(' ')
68
+
69
+ collect_diagnose_data_script_path = File.expand_path("#{__dir__}/scripts/collect_simctl_diagnose_data.sh")
70
+
71
+ shell_helper.sh(
72
+ command: "SIMCTL_DIAGNOSE_OUTPUT_FOLDER=#{temp_output_dir.shellescape} #{collect_diagnose_data_script_path.shellescape} #{cmd_args}",
73
+ print_command: verbose,
74
+ print_command_output: verbose
75
+ )
76
+
77
+ archive_name = "#{temp_output_dir}.tar.gz"
78
+ Actions.lane_context[Actions::SharedValues::GATHER_SIMCTL_DIAGNOSE_OUTPUT_FILE] = archive_name
79
+
80
+ archive_name
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env expect
2
+
3
+ # Runs xcrun simctl diagnose with given parameters.
4
+ # xcrun simctl diagnose needs user to press ENTER which is not possible to do in non-interactive shell.
5
+
6
+ spawn xcrun simctl diagnose {*}$argv
7
+ expect -exact "Press ENTER to continue\r\n"
8
+ send -- "\r"
9
+ expect eof
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # Runs xcrun simctl diagnose with given parameters.
4
+ # Manages the output folder and the archive.
5
+ #
6
+ # When --data-container is passed simctl diagnose doesn't create archive,
7
+ # and w/o archive the artifacts step on CI hangs.
8
+ #
9
+ # For this case we handle the archive creation manually.
10
+
11
+ set -Eeo pipefail
12
+
13
+ export SIMCTL_DIAGNOSE_OUTPUT_FOLDER="${SIMCTL_DIAGNOSE_OUTPUT_FOLDER:-"logs/simctl_diagnose"}"
14
+
15
+ FOLDER_NAME="$(basename "${SIMCTL_DIAGNOSE_OUTPUT_FOLDER}")"
16
+ OUTPUT_FOLDER_PATH="$(dirname "${SIMCTL_DIAGNOSE_OUTPUT_FOLDER}")"
17
+
18
+ echo "Cleaning up previous simctl logs at ${SIMCTL_DIAGNOSE_OUTPUT_FOLDER}"
19
+ rm -rf "${SIMCTL_DIAGNOSE_OUTPUT_FOLDER}" || true
20
+ rm -rf "${OUTPUT_FOLDER_PATH}/${FOLDER_NAME}."* || true
21
+ mkdir -p "${SIMCTL_DIAGNOSE_OUTPUT_FOLDER}"
22
+
23
+ echo "Collecting simctl logs to ${SIMCTL_DIAGNOSE_OUTPUT_FOLDER}..."
24
+ "$(dirname "${0}")/$(basename "${0}" .sh).expect" "${@}" --output="${SIMCTL_DIAGNOSE_OUTPUT_FOLDER}"
25
+
26
+ if [ -d "${SIMCTL_DIAGNOSE_OUTPUT_FOLDER}" ]; then
27
+ echo "Compressing simctl logs to ${SIMCTL_DIAGNOSE_OUTPUT_FOLDER}.tar.gz..."
28
+ cd "${OUTPUT_FOLDER_PATH}"
29
+ tar -czf "${FOLDER_NAME}.tar.gz" "${FOLDER_NAME}"
30
+ echo "Removing folder ${FOLDER_NAME}"
31
+ # Remove folder becuse it contains symlinks and zip doesn't remove them and tar doesn't remove it at all.
32
+ rm -rf "${FOLDER_NAME}" || true
33
+ echo "Archive size: $(du -h "${FOLDER_NAME}.tar.gz" | cut -f1)"
34
+ elif [ -f "${SIMCTL_DIAGNOSE_OUTPUT_FOLDER}.zip" ]; then
35
+ echo "Archive size: $(du -h "${SIMCTL_DIAGNOSE_OUTPUT_FOLDER}.zip" | cut -f1)"
36
+ elif [ -f "${SIMCTL_DIAGNOSE_OUTPUT_FOLDER}.tar.gz" ]; then
37
+ echo "Archive size: $(du -h "${SIMCTL_DIAGNOSE_OUTPUT_FOLDER}.tar.gz" | cut -f1)"
38
+ fi
39
+
40
+ echo "Simctl logs collected successfully at $OUTPUT_FOLDER_PATH"
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fastlane'
4
+
5
+ module Fastlane
6
+ module Actions
7
+ module SharedValues
8
+ GATHER_SIMCTL_DIAGNOSE_OUTPUT_FILE = :GATHER_SIMCTL_DIAGNOSE_OUTPUT_FILE
9
+ end
10
+ end
11
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Fastlane
4
4
  module CreateSimulatorDevices
5
- VERSION = '0.0.12'
5
+ VERSION = '0.0.14'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fastlane-plugin-create_simulator_devices
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.12
4
+ version: 0.0.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vitalii Budnik
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-08-29 00:00:00.000000000 Z
11
+ date: 2025-09-04 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email: developers@setapp.com
@@ -20,8 +20,10 @@ files:
20
20
  - README.md
21
21
  - lib/fastlane/plugin/create_simulator_devices.rb
22
22
  - lib/fastlane/plugin/create_simulator_devices/actions/create_simulator_devices_action.rb
23
+ - lib/fastlane/plugin/create_simulator_devices/actions/gather_simctl_diagnose_action.rb
23
24
  - lib/fastlane/plugin/create_simulator_devices/helpers/create_simulator_devices/models.rb
24
25
  - lib/fastlane/plugin/create_simulator_devices/helpers/create_simulator_devices/models/apple_build_version.rb
26
+ - lib/fastlane/plugin/create_simulator_devices/helpers/create_simulator_devices/models/device_naming_style.rb
25
27
  - lib/fastlane/plugin/create_simulator_devices/helpers/create_simulator_devices/models/required_device.rb
26
28
  - lib/fastlane/plugin/create_simulator_devices/helpers/create_simulator_devices/models/required_runtime.rb
27
29
  - lib/fastlane/plugin/create_simulator_devices/helpers/create_simulator_devices/models/simctl/device.rb
@@ -33,6 +35,11 @@ files:
33
35
  - lib/fastlane/plugin/create_simulator_devices/helpers/create_simulator_devices/runtime_helper.rb
34
36
  - lib/fastlane/plugin/create_simulator_devices/helpers/create_simulator_devices/shared_values.rb
35
37
  - lib/fastlane/plugin/create_simulator_devices/helpers/create_simulator_devices/shell_helper.rb
38
+ - lib/fastlane/plugin/create_simulator_devices/helpers/gather_simctl_diagnose/module.rb
39
+ - lib/fastlane/plugin/create_simulator_devices/helpers/gather_simctl_diagnose/runner.rb
40
+ - lib/fastlane/plugin/create_simulator_devices/helpers/gather_simctl_diagnose/scripts/collect_simctl_diagnose_data.expect
41
+ - lib/fastlane/plugin/create_simulator_devices/helpers/gather_simctl_diagnose/scripts/collect_simctl_diagnose_data.sh
42
+ - lib/fastlane/plugin/create_simulator_devices/helpers/gather_simctl_diagnose/shared_values.rb
36
43
  - lib/fastlane/plugin/create_simulator_devices/version.rb
37
44
  homepage:
38
45
  licenses: