fastlane-plugin-create_simulator_devices 0.0.11 → 0.0.13

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 97c45d507e589509620dd5bfc35a27ae50948ebcba6c886b056b940b3da84a4a
4
- data.tar.gz: cc4c4d2c65c49341a01f28c36df7c99713b78c493ad66272fad69570feaf2b7d
3
+ metadata.gz: fbcf5d008c0e2e248db6b53d80c89c681cd516837439e1da34e95375c135e934
4
+ data.tar.gz: adc56432be831c59c299204092d96392f3a9483d08ae625a6c5d31695d58a3f0
5
5
  SHA512:
6
- metadata.gz: bf6e1da7e6f5a89b38f0ae6a180e2a1d9cda9c5bbf7fcdb1bc1bad8e98e947acf7bff35818ec5eb435e676fe486efa5b28ffa6e6c9dd8c1cb59dea8184c1a907
7
- data.tar.gz: c0ad977064e5be87a3d0003db5de5ff10592fe40792c1c718209dd61f9df0498a7ac2eda1ed659f5529ebcfa619a04891608c3cab90072f64e43f91a01e71eba
6
+ metadata.gz: ded9ef76090109dd7f69d96384625e3899bed635d8d59aee8213954cfe33003be2874de36b0b8aae3bf16cf7e10a27bd59adfd51a9cd4d7d3a2600061bf39d8e
7
+ data.tar.gz: 02cf2720d898e0cb315bb37d13eb1d86a9b10815635def22781465a3a268e1119b836bf39da22793b4c07d317565f67f118a329511035d478865ab3b3accf285
@@ -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,15 @@ 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
31
35
  )
32
36
 
33
37
  runner.run(required_devices)
@@ -52,7 +56,7 @@ module Fastlane
52
56
  )"
53
57
  end
54
58
 
55
- def self.available_options
59
+ def self.available_options # rubocop:disable Metrics/MethodLength
56
60
  [
57
61
  ::FastlaneCore::ConfigItem.new(key: :devices,
58
62
  env_name: 'SCAN_DEVICES',
@@ -80,7 +84,29 @@ module Fastlane
80
84
  description: 'Print xcrun simctl commands output',
81
85
  type: Boolean,
82
86
  optional: true,
83
- default_value: false)
87
+ default_value: false),
88
+ ::FastlaneCore::ConfigItem.new(key: :rename_devices,
89
+ env_name: 'CREATE_SIMULATOR_DEVICES_RENAME_DEVICES',
90
+ description: 'Rename devices if needed',
91
+ type: Boolean,
92
+ optional: true,
93
+ default_value: false),
94
+ ::FastlaneCore::ConfigItem.new(key: :delete_duplicate_devices,
95
+ env_name: 'CREATE_SIMULATOR_DEVICES_DELETE_DUPLICATE_DEVICES',
96
+ description: 'Delete duplicate devices',
97
+ type: Boolean,
98
+ optional: true,
99
+ default_value: false),
100
+ ::FastlaneCore::ConfigItem.new(key: :device_naming_style,
101
+ env_name: 'CREATE_SIMULATOR_DEVICES_DEVICE_NAMING_STYLE',
102
+ description: 'Device naming style',
103
+ type: String,
104
+ optional: true,
105
+ default_value: CreateSimulatorDevices::DeviceNamingStyle::SCAN.to_s,
106
+ verify_block: proc do |value|
107
+ allowed_values = CreateSimulatorDevices::DeviceNamingStyle::ALL
108
+ UI.user_error!("Invalid device naming style: #{value}. Allowed values: #{allowed_values.map(&:to_s).join(', ')}") unless allowed_values.include?(value.to_sym)
109
+ end)
84
110
  ]
85
111
  end
86
112
 
@@ -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
@@ -4,14 +4,14 @@ module Fastlane
4
4
  module CreateSimulatorDevices
5
5
  # Represents a required runtime.
6
6
  class RequiredDevice
7
- attr_accessor :device_type, :os_name, :required_runtime, :available_runtime, :available_device
7
+ attr_accessor :device_type, :os_name, :required_runtime, :simctl_runtime, :simctl_device
8
8
 
9
- def initialize(device_type:, os_name:, required_runtime:, available_runtime:, available_device:)
9
+ def initialize(device_type:, os_name:, required_runtime:, simctl_runtime:, simctl_device:)
10
10
  self.device_type = device_type
11
11
  self.os_name = os_name
12
12
  self.required_runtime = required_runtime
13
- self.available_runtime = available_runtime
14
- self.available_device = available_device
13
+ self.simctl_runtime = simctl_runtime
14
+ self.simctl_device = simctl_device
15
15
  end
16
16
 
17
17
  def description
@@ -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,24 @@
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
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:) # 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
20
24
  end
21
25
 
22
26
  def run(devices)
@@ -46,14 +50,16 @@ module Fastlane
46
50
 
47
51
  # Return distinct matched devices strings
48
52
  matched_devices = required_devices
49
- .reject { |required_device| required_device.available_device.nil? }
53
+ .reject { |required_device| required_device.simctl_device.nil? }
50
54
 
51
55
  log_matched_devices(matched_devices: matched_devices)
52
56
 
53
- available_simulator_devices = matched_devices.map(&:available_device)
54
- Actions.lane_context[Actions::SharedValues::AVAILABLE_SIMULATOR_DEVICES] = available_simulator_devices
57
+ matched_devices_names = matched_devices.map { |matched_device| returning_device_name_for_required_device(matched_device) }
58
+ UI.message("Available simulator devices: #{matched_devices_names.join(', ')}")
55
59
 
56
- matched_devices.map(&:description)
60
+ Actions.lane_context[Actions::SharedValues::AVAILABLE_SIMULATOR_DEVICES] = matched_devices_names
61
+
62
+ matched_devices_names
57
63
  end
58
64
 
59
65
  def log_matched_devices(matched_devices:)
@@ -63,46 +69,118 @@ module Fastlane
63
69
  matched_devices.each do |matched_device|
64
70
  device_info = ''
65
71
  if verbose
66
- device_info = shell_helper.device_info_by_udid(matched_device.available_device.udid)
72
+ device_info = shell_helper.device_info_by_udid(matched_device.simctl_device.udid)
67
73
  device_info = "\n#{device_info}"
68
74
  end
69
- UI.message(" #{matched_device.description}: #{matched_device.available_device.description}#{device_info}")
75
+ UI.message(" #{matched_device.description}: #{matched_device.simctl_device.description}#{device_info}")
70
76
  end
71
-
72
- matched_devices_names = matched_devices
73
- .map { |matched_device| matched_device.available_device.name }
74
- .join(', ')
75
- UI.message("Available simulator devices: #{matched_devices_names}")
76
77
  end
77
78
 
78
79
  def delete_unavailable_devices
79
- return unless shell_helper.available_devices_for_runtimes.values.flatten.any?(&:available?)
80
+ return unless shell_helper.simctl_devices_for_runtimes.values.flatten.any?(&:available?)
80
81
 
81
82
  shell_helper.delete_unavailable_devices
82
83
 
83
- shell_helper.available_devices_for_runtimes(force: true)
84
+ shell_helper.simctl_devices_for_runtimes(force: true)
84
85
  end
85
86
 
86
- def available_device_for_required_device(required_device)
87
- return nil if required_device.available_runtime.nil?
87
+ def simctl_device_for_required_device(required_device)
88
+ return nil if required_device.simctl_runtime.nil?
88
89
 
89
- available_devices = shell_helper.available_devices_for_runtimes[required_device.available_runtime.identifier.to_sym]
90
+ simctl_devices = shell_helper.simctl_devices_for_runtimes[required_device.simctl_runtime.identifier.to_sym]
90
91
 
91
- return [] if available_devices.nil?
92
+ return nil if simctl_devices.nil?
92
93
 
93
94
  # Find the device with the same name as the required device.
94
- devices_with_same_type = available_devices
95
- .select { |available_device| available_device.device_type_identifier == required_device.device_type.identifier }
95
+ devices_with_same_type = simctl_devices
96
+ .select { |simctl_device| simctl_device.device_type_identifier == required_device.device_type.identifier }
97
+
98
+ preferred_device_name = device_name_for_required_device(required_device)
99
+
100
+ # Prefer device with the same name that includes the runtime version.
101
+ matching_device = devices_with_same_type.detect { |simctl_device| simctl_device.name == preferred_device_name }
102
+
103
+ if can_rename_devices
104
+ # Otherwise, if rename is enabled, use the first device with the same type.
105
+ matching_device ||= devices_with_same_type.first
106
+ rename_device_if_needed(matching_device, preferred_device_name)
107
+ end
108
+
109
+ delete_duplicate_devices(devices_with_same_type, matching_device) if can_delete_duplicate_devices
110
+
111
+ matching_device
112
+ end
113
+
114
+ def rename_device_if_needed(matching_device, preferred_device_name)
115
+ return if matching_device.nil? || matching_device.name == preferred_device_name
96
116
 
97
- devices_with_same_type
98
- .detect { |available_device| available_device.name == required_device.device_type.name }
117
+ UI.message("Renaming device #{matching_device.name} (udid: #{matching_device.udid}) to #{preferred_device_name}")
118
+ shell_helper.rename_device(udid: matching_device.udid, name: preferred_device_name)
119
+ matching_device.name = preferred_device_name
120
+ end
121
+
122
+ def delete_duplicate_devices(matching_devices, matching_device)
123
+ return if matching_device.nil?
124
+
125
+ matching_devices
126
+ .reject { |simctl_device| simctl_device.udid == matching_device.udid }
127
+ .each do |simctl_device|
128
+ UI.message("Deleting duplicate device #{simctl_device.name} (udid: #{simctl_device.udid})")
129
+ shell_helper.delete_device(udid: simctl_device.udid)
130
+ end
131
+ end
132
+
133
+ # Returns the device name for the required device.
134
+ #
135
+ # This name is used in the simctl device name.
136
+ def device_name_for_required_device(required_device)
137
+ case device_naming_style
138
+ when DeviceNamingStyle::SCAN, DeviceNamingStyle::RUN_TESTS
139
+ # scan modifies the device name by removing the runtime version when searching for a passed device name.
140
+ # E.g.:
141
+ # * given "iPhone 15 (17.0)" match will search for simulator named "iPhone 15" with the SDK version 17.0.
142
+ # * given "iPhone 15" match will search for simulator named "iPhone 15" with the default SDK version.
143
+ # So we need to name the device by the device type name for scan to find the correct device.
144
+ required_device.device_type.name
145
+ when DeviceNamingStyle::SNAPSHOT, DeviceNamingStyle::CAPTURE_IOS_SCREENSHOTS
146
+ # snapshot nither does not modify the device name when searching for a passed device name nor extracts the runtime version from the device name.
147
+ # E.g.:
148
+ # * given "iPhone 15 (17.0)" match will search for device named exactly "iPhone 15 (17.0)".
149
+ # * given "iPhone 15" match will search for device named exactly "iPhone 15".
150
+ # So we need to return the full device name for the required device for snapshot to find the correct device.
151
+ runtime_build = required_device.simctl_runtime.build_version
152
+ build_suffix = runtime_build.beta? ? "-#{runtime_build}" : ''
153
+ "#{required_device.device_type.name} (#{required_device.simctl_runtime.version}#{build_suffix})"
154
+ end
155
+ end
156
+
157
+ # Returns the device name for the required device.
158
+ #
159
+ # This name is used when passing the device name to the scan or snapshot.
160
+ def returning_device_name_for_required_device(required_device)
161
+ case device_naming_style
162
+ when DeviceNamingStyle::SCAN, DeviceNamingStyle::RUN_TESTS
163
+ # 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.
164
+ # E.g.:
165
+ # * given "iPhone 15 (17.0)" match will search for simulator named "iPhone 15" with the SDK version 17.0.
166
+ # * given "iPhone 15" match will search for simulator named "iPhone 15" with the default SDK version.
167
+ # So we need to return the device name and the required runtime version for scan to find the correct device.
168
+ "#{required_device.simctl_device.name} (#{required_device.required_runtime.product_version})"
169
+ when DeviceNamingStyle::SNAPSHOT, DeviceNamingStyle::CAPTURE_IOS_SCREENSHOTS
170
+ # snapshot does not modify the device name when searching for a passed device name nor extracts the runtime version from the device name.
171
+ # E.g.:
172
+ # * given "iPhone 15 (17.0)" match will search for device named exactly "iPhone 15 (17.0)".
173
+ # * given "iPhone 15" match will search for device named exactly "iPhone 15" .
174
+ # So we need to return the full device name for the required device for snapshot to find the correct device.
175
+ required_device.simctl_device.name
176
+ end
99
177
  end
100
178
 
101
179
  def create_missing_devices(required_devices)
102
180
  find_runtime_and_device_for_required_devices(required_devices)
103
181
 
104
182
  missing_devices = required_devices
105
- .select { |required_device| required_device.available_device.nil? }
183
+ .select { |required_device| required_device.simctl_device.nil? }
106
184
 
107
185
  if missing_devices.empty?
108
186
  UI.message('All required devices are present. Skipping device creation...') if verbose
@@ -112,13 +190,13 @@ module Fastlane
112
190
  UI.message('Creating missing devices')
113
191
  missing_devices.each do |missing_device|
114
192
  shell_helper.create_device(
115
- missing_device.device_type.name,
193
+ device_name_for_required_device(missing_device),
116
194
  missing_device.device_type.identifier,
117
- missing_device.available_runtime.identifier
195
+ missing_device.simctl_runtime.identifier
118
196
  )
119
197
  end
120
198
 
121
- shell_helper.available_devices_for_runtimes(force: true)
199
+ shell_helper.simctl_devices_for_runtimes(force: true)
122
200
 
123
201
  find_runtime_and_device_for_required_devices(missing_devices)
124
202
  end
@@ -126,8 +204,8 @@ module Fastlane
126
204
  def find_runtime_and_device_for_required_devices(required_devices)
127
205
  UI.message('Searching for matching available devices...')
128
206
  required_devices.each do |required_device|
129
- required_device.available_runtime = runtime_helper.available_runtime_for_required_device(required_device)
130
- required_device.available_device = available_device_for_required_device(required_device)
207
+ required_device.simctl_runtime = runtime_helper.simctl_runtime_for_required_device(required_device)
208
+ required_device.simctl_device = simctl_device_for_required_device(required_device)
131
209
  end
132
210
  end
133
211
 
@@ -141,17 +219,17 @@ module Fastlane
141
219
  }.freeze
142
220
 
143
221
  def required_device_for_device(device)
144
- available_device_types = shell_helper.available_device_types
222
+ simctl_device_types = shell_helper.simctl_device_types
145
223
 
146
224
  device_os_version = device[/ \(([\d.]*?)\)$/, 1]
147
225
  device_name = device_os_version.nil? ? device : device.delete_suffix(" (#{device_os_version})").strip
148
226
 
149
- device_type = available_device_types.detect do |available_device_type|
150
- device_name == available_device_type.name
227
+ device_type = simctl_device_types.detect do |simctl_device_type|
228
+ device_name == simctl_device_type.name
151
229
  end
152
230
 
153
231
  unless device_type
154
- UI.important("Device type not found for device #{device}. Available device types: #{available_device_types.map(&:name).join(', ')}.")
232
+ UI.important("Device type not found for device #{device}. Available device types: #{simctl_device_types.map(&:name).join(', ')}.")
155
233
  return nil
156
234
  end
157
235
 
@@ -170,8 +248,8 @@ module Fastlane
170
248
  device_type:,
171
249
  os_name:,
172
250
  required_runtime: nil,
173
- available_runtime: nil,
174
- available_device: nil
251
+ simctl_runtime: nil,
252
+ simctl_device: nil
175
253
  )
176
254
 
177
255
  required_device.required_runtime = runtime_helper.required_runtime_for_device(required_device, runtime_version)
@@ -35,8 +35,8 @@ module Fastlane
35
35
  shell_helper.delete_runtime(runtime.identifier)
36
36
  end
37
37
 
38
- shell_helper.available_runtimes(force: true)
39
- shell_helper.available_devices_for_runtimes(force: true)
38
+ shell_helper.simctl_runtimes(force: true)
39
+ shell_helper.simctl_devices_for_runtimes(force: true)
40
40
  end
41
41
 
42
42
  def install_missing_runtimes(required_devices)
@@ -53,10 +53,10 @@ module Fastlane
53
53
  download_and_install_missing_runtime(missing_runtime)
54
54
  end
55
55
 
56
- # Update available_runtimes after installing the runtimes.
56
+ # Update simctl_runtimes after installing the runtimes.
57
57
  shell_helper.installed_runtimes_with_state
58
- shell_helper.available_runtimes(force: true)
59
- shell_helper.available_devices_for_runtimes(force: true)
58
+ shell_helper.simctl_runtimes(force: true)
59
+ shell_helper.simctl_devices_for_runtimes(force: true)
60
60
 
61
61
  # Check if missing runtimes are available after installing
62
62
  missing_runtimes = missing_runtimes(missing_runtimes)
@@ -71,21 +71,21 @@ module Fastlane
71
71
  def missing_runtimes(needed_runtimes)
72
72
  needed_runtimes.select do |needed_runtime|
73
73
  # Check if available runtimes contain the needed runtime.
74
- available_runtime_matching_needed_runtime?(needed_runtime).nil?
74
+ simctl_runtime_matching_needed_runtime?(needed_runtime).nil?
75
75
  end
76
76
  end
77
77
 
78
- def available_runtime_matching_needed_runtime?(needed_runtime)
79
- matching_runtimes = shell_helper.available_runtimes
80
- .select do |available_runtime|
81
- next false if needed_runtime.os_name != available_runtime.platform
78
+ def simctl_runtime_matching_needed_runtime?(needed_runtime)
79
+ matching_runtimes = shell_helper.simctl_runtimes
80
+ .select do |simctl_runtime|
81
+ next false if needed_runtime.os_name != simctl_runtime.platform
82
82
 
83
83
  # If the product version is not equal, check if the first two segments are equal.
84
- is_product_version_equal = if needed_runtime.product_version == available_runtime.version
84
+ is_product_version_equal = if needed_runtime.product_version == simctl_runtime.version
85
85
  true
86
86
  else
87
87
  lhs_segments = needed_runtime.product_version.segments
88
- rhs_segments = available_runtime.version.segments
88
+ rhs_segments = simctl_runtime.version.segments
89
89
  if rhs_segments.size == 3
90
90
  lhs_segments[0] == rhs_segments[0] && lhs_segments[1] == rhs_segments[1]
91
91
  else
@@ -95,37 +95,37 @@ module Fastlane
95
95
  next false unless is_product_version_equal
96
96
 
97
97
  # If the product version is not equal, use the available runtime version.
98
- needed_runtime.product_version = available_runtime.version
98
+ needed_runtime.product_version = simctl_runtime.version
99
99
 
100
- needed_runtime.product_build_version = [needed_runtime.product_build_version, available_runtime.build_version].compact.max
100
+ needed_runtime.product_build_version = [needed_runtime.product_build_version, simctl_runtime.build_version].compact.max
101
101
 
102
- needed_runtime.product_build_version.almost_equal?(available_runtime.build_version)
102
+ needed_runtime.product_build_version.almost_equal?(simctl_runtime.build_version)
103
103
  end
104
104
 
105
- matching_runtimes.max_by { |available_runtime| [available_runtime.version, available_runtime.build_version] }
105
+ matching_runtimes.max_by { |simctl_runtime| [simctl_runtime.version, simctl_runtime.build_version] }
106
106
  end
107
107
 
108
- def available_runtime_for_required_device(required_device)
109
- available_runtime = available_runtime_matching_needed_runtime?(required_device.required_runtime)
108
+ def simctl_runtime_for_required_device(required_device)
109
+ simctl_runtime = simctl_runtime_matching_needed_runtime?(required_device.required_runtime)
110
110
 
111
- if available_runtime.nil?
111
+ if simctl_runtime.nil?
112
112
  UI.important("Runtime #{required_device.required_runtime.description} not found. Skipping simulator creation for #{required_device.description}...")
113
113
  return nil
114
114
  end
115
115
 
116
116
  # Check if the runtime supports the device type.
117
- if available_runtime.supported_device_types
117
+ if simctl_runtime.supported_device_types
118
118
  .none? { |supported_device_type| supported_device_type.identifier == required_device.device_type.identifier }
119
- UI.important("Device type #{required_device.device_type.name} is not supported by runtime #{available_runtime.identifier}. Skipping simulator creation for #{required_device.description}...")
119
+ UI.important("Device type #{required_device.device_type.name} is not supported by runtime #{simctl_runtime.identifier}. Skipping simulator creation for #{required_device.description}...")
120
120
  return nil
121
121
  end
122
122
 
123
- if available_runtime.nil?
123
+ if simctl_runtime.nil?
124
124
  UI.important("Runtime #{required_device.required_runtime.description} not found. Skipping simulator creation for #{required_device.description}...")
125
125
  return nil
126
126
  end
127
127
 
128
- available_runtime
128
+ simctl_runtime
129
129
  end
130
130
 
131
131
  def download_and_install_missing_runtime(missing_runtime)
@@ -161,7 +161,12 @@ module Fastlane
161
161
  # shipped with Xcode betas and use the same product version.
162
162
  # E.g. Xcode 26.0 Beta 3 has iOS 26.0 (23A5287e) SDK, but
163
163
  # 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
164
+ product_build_version = missing_runtime.product_build_version
165
+ if product_build_version
166
+ runtime_dmg_search_pattern += product_build_version.major.to_s
167
+ runtime_dmg_search_pattern += product_build_version.minor.to_s
168
+ runtime_dmg_search_pattern += product_build_version.patch.to_s
169
+ end
165
170
  runtime_dmg_search_pattern += '*.dmg'
166
171
 
167
172
  if verbose
@@ -184,7 +189,7 @@ module Fastlane
184
189
  end
185
190
 
186
191
  def required_runtime_for_device(required_device, runtime_version)
187
- sdk = max_available_simulator_sdks[required_device.os_name]
192
+ sdk = max_xcodebuild_simulator_sdks[required_device.os_name]
188
193
 
189
194
  # If the runtime version is the same as the SDK version, use the SDK build version.
190
195
  # This will allow to use different runtimes for the same version but different Xcode beta versions.
@@ -205,10 +210,10 @@ module Fastlane
205
210
  end
206
211
 
207
212
  # Returns a hash where key is platform string and value is sdk version.
208
- def max_available_simulator_sdks
209
- return @max_available_simulator_sdks unless @max_available_simulator_sdks.nil?
213
+ def max_xcodebuild_simulator_sdks
214
+ return @max_xcodebuild_simulator_sdks unless @max_xcodebuild_simulator_sdks.nil?
210
215
 
211
- @max_available_simulator_sdks = shell_helper.available_sdks
216
+ @max_xcodebuild_simulator_sdks = shell_helper.xcodebuild_sdks
212
217
  # Only simulators
213
218
  .filter { |sdk| sdk.platform.include?('simulator') }
214
219
  # Calculate max version for each product name
@@ -218,7 +223,7 @@ module Fastlane
218
223
  sdk_versions[os_name] = sdk if stored_sdk.nil? || sdk.product_version > stored_sdk.product_version
219
224
  end
220
225
 
221
- @max_available_simulator_sdks
226
+ @max_xcodebuild_simulator_sdks
222
227
  end
223
228
  end
224
229
  end
@@ -53,20 +53,20 @@ module Fastlane
53
53
  end
54
54
  end
55
55
 
56
- def available_device_types(force: false)
57
- return @available_device_types unless force || @available_device_types.nil?
56
+ def simctl_device_types(force: false)
57
+ return @simctl_device_types unless force || @simctl_device_types.nil?
58
58
 
59
59
  UI.message('Fetching available device types...')
60
60
  json = sh(command: 'xcrun simctl list --json --no-escape-slashes devicetypes')
61
61
 
62
- @available_device_types = JSON
62
+ @simctl_device_types = JSON
63
63
  .parse(json, symbolize_names: true)[:devicetypes]
64
64
  .map { |device_type| SimCTL::DeviceType.from_hash(device_type) }
65
65
 
66
- @available_device_types
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
@@ -76,31 +76,31 @@ module Fastlane
76
76
  sh(command: 'xcrun simctl delete unavailable')
77
77
  end
78
78
 
79
- def available_devices_for_runtimes(force: false)
80
- return @available_devices_for_runtimes unless force || @available_devices_for_runtimes.nil?
79
+ def simctl_devices_for_runtimes(force: false)
80
+ return @simctl_devices_for_runtimes unless force || @simctl_devices_for_runtimes.nil?
81
81
 
82
82
  UI.message('Fetching available devices...')
83
83
  json = sh(command: 'xcrun simctl list --json --no-escape-slashes devices')
84
84
 
85
- @available_devices_for_runtimes = JSON
85
+ @simctl_devices_for_runtimes = JSON
86
86
  .parse(json, symbolize_names: true)[:devices]
87
87
  .transform_values { |devices| devices.map { |device| SimCTL::Device.from_hash(device) } }
88
88
 
89
- @available_devices_for_runtimes
89
+ @simctl_devices_for_runtimes
90
90
  end
91
91
 
92
- def available_runtimes(force: false)
93
- return @available_runtimes unless force || @available_runtimes.nil?
92
+ def simctl_runtimes(force: false)
93
+ return @simctl_runtimes unless force || @simctl_runtimes.nil?
94
94
 
95
95
  UI.message('Fetching available runtimes...')
96
96
  json = sh(command: 'xcrun simctl list --json --no-escape-slashes runtimes')
97
97
 
98
- @available_runtimes = JSON
98
+ @simctl_runtimes = JSON
99
99
  .parse(json, symbolize_names: true)[:runtimes]
100
100
  .map { |runtime| SimCTL::Runtime.from_hash(runtime) }
101
101
  .select(&:available?)
102
102
 
103
- @available_runtimes
103
+ @simctl_runtimes
104
104
  end
105
105
 
106
106
  def installed_runtimes_with_state
@@ -112,17 +112,17 @@ module Fastlane
112
112
  .map { |_, runtime| SimCTL::RuntimeWithState.from_hash(runtime) }
113
113
  end
114
114
 
115
- def available_sdks(force: false)
116
- return @available_sdks unless force || @available_sdks.nil?
115
+ def xcodebuild_sdks(force: false)
116
+ return @xcodebuild_sdks unless force || @xcodebuild_sdks.nil?
117
117
 
118
118
  UI.message('Fetching available sdks...')
119
119
  json = sh(command: 'xcrun xcodebuild -showsdks -json')
120
120
 
121
- @available_sdks = JSON
121
+ @xcodebuild_sdks = JSON
122
122
  .parse(json, symbolize_names: true)
123
123
  .map { |sdk| Xcodebuild::SDK.from_hash(sdk) }
124
124
 
125
- @available_sdks
125
+ @xcodebuild_sdks
126
126
  end
127
127
 
128
128
  def create_device(name, device_type_identifier, runtime_identifier)
@@ -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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Fastlane
4
4
  module CreateSimulatorDevices
5
- VERSION = '0.0.11'
5
+ VERSION = '0.0.13'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
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.11
4
+ version: 0.0.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vitalii Budnik
@@ -22,6 +22,7 @@ files:
22
22
  - lib/fastlane/plugin/create_simulator_devices/actions/create_simulator_devices_action.rb
23
23
  - lib/fastlane/plugin/create_simulator_devices/helpers/create_simulator_devices/models.rb
24
24
  - lib/fastlane/plugin/create_simulator_devices/helpers/create_simulator_devices/models/apple_build_version.rb
25
+ - lib/fastlane/plugin/create_simulator_devices/helpers/create_simulator_devices/models/device_naming_style.rb
25
26
  - lib/fastlane/plugin/create_simulator_devices/helpers/create_simulator_devices/models/required_device.rb
26
27
  - lib/fastlane/plugin/create_simulator_devices/helpers/create_simulator_devices/models/required_runtime.rb
27
28
  - lib/fastlane/plugin/create_simulator_devices/helpers/create_simulator_devices/models/simctl/device.rb