fastlane-plugin-create_simulator_devices 0.0.4 → 0.0.6

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: 53aed494d69e050f8014a9b188cf5dd2fb3475c5d8e44b7d22e21eb8949d66aa
4
- data.tar.gz: 315fe5620703cde142b5ae0aff18cc90989398e96e2587dab89f1030bcf0ece1
3
+ metadata.gz: a77a05dbf6d484a920978945f9e5941b67ec86f549dfeaa7d9add45d1e778174
4
+ data.tar.gz: '00961903381d91b9170c63bc51ebb2bbea55111511856eea951c854834d77b37'
5
5
  SHA512:
6
- metadata.gz: 68539faced67b76306b818ccb1cc3172c00245e903e234085852c22db09c683907bc94f024238c73a31c1edd869d481df7b6a174a2cf996817fa4acc598b9010
7
- data.tar.gz: ba14fdf8693f60557079f96eead7e46cf5730f6e0cc86f7d72477185b0acbd4367851562789b0d095336104d4bc3b1dee2547fb9863cbcac041b2d650ec277e7
6
+ metadata.gz: be42b6fb3d60074b6b24c738c53cba15516c599b7f7ac80dce53a0a8dc7e845c2855de0ed9175487daae482c237af90a0b1d93954a4eded31387e4ea0359ed39
7
+ data.tar.gz: c0c89fe57b0992f5d2b1dbae8821e64f3719836500c01c7eaf5442c6bf5b5feba4c99ce59ef4d0b89b9950d2c44fd25a0e24d2db33f29f86e3de22dc2044558c
@@ -25,7 +25,7 @@ module Fastlane
25
25
  required_devices = params[:devices]
26
26
  UI.user_error!('No devices specified') if required_devices.nil? || required_devices.empty?
27
27
 
28
- shell_helper = CreateSimulatorDevices::ShellHelper.new(verbose:, action_context: self)
28
+ shell_helper = CreateSimulatorDevices::ShellHelper.new(print_command: params[:print_command], print_command_output: params[:print_command_output], action_context: self)
29
29
  runtime_helper = CreateSimulatorDevices::RuntimeHelper.new(cache_dir: params[:cache_dir], shell_helper:, verbose:)
30
30
 
31
31
  runner = CreateSimulatorDevices::Runner.new(
@@ -69,16 +69,26 @@ module Fastlane
69
69
  default_value: 'iPhone 16'),
70
70
  ::FastlaneCore::ConfigItem.new(key: :cache_dir,
71
71
  description: 'The directory to cache the simulator runtimes',
72
- is_string: true,
72
+ type: String,
73
73
  optional: true,
74
74
  default_value: "#{Dir.home}/.cache/create_simulator_devices"),
75
75
  ::FastlaneCore::ConfigItem.new(key: :verbose,
76
76
  env_name: 'VERBOSE',
77
77
  description: 'Verbose output',
78
- is_string: false,
78
+ type: Boolean,
79
79
  optional: true,
80
80
  default_value: ::FastlaneCore::Globals.verbose?,
81
- default_value_dynamic: true)
81
+ default_value_dynamic: true),
82
+ ::FastlaneCore::ConfigItem.new(key: :print_command,
83
+ description: 'Print xcrun simctl commands',
84
+ type: Boolean,
85
+ optional: true,
86
+ default_value: false),
87
+ ::FastlaneCore::ConfigItem.new(key: :print_command_output,
88
+ description: 'Print xcrun simctl commands output',
89
+ type: Boolean,
90
+ optional: true,
91
+ default_value: false)
82
92
  ]
83
93
  end
84
94
 
@@ -58,7 +58,12 @@ module Fastlane
58
58
 
59
59
  return self == other unless lhs_is_beta && rhs_is_beta && lhs_build_version.length == rhs_build_version.length
60
60
 
61
- lhs_build_version.chop == rhs_build_version.chop
61
+ # Take only leading chars up to the first letter included e.g. 22C146 -> 22C
62
+ lhs_build_version.minor_version == rhs_build_version.minor_version
63
+ end
64
+
65
+ def minor_version
66
+ AppleBuildVersion.new(@build_version.match(/^[0-9]+[A-Z]+/)[0])
62
67
  end
63
68
 
64
69
  def <(other)
@@ -18,6 +18,8 @@ module Fastlane
18
18
  end
19
19
 
20
20
  def run(devices)
21
+ UI.message("Simulator devices to create: #{devices.join(', ')}")
22
+
21
23
  shell_helper.stop_core_simulator_services
22
24
 
23
25
  # Delete unusable runtimes and unavailable devices.
@@ -29,6 +31,11 @@ module Fastlane
29
31
  .filter_map { |device| required_device_for_device(device) }
30
32
  .uniq { |required_device| [required_device.device_type.name, required_device.required_runtime.product_version] }
31
33
 
34
+ if verbose
35
+ UI.message('Unique required devices:')
36
+ UI.message(" #{required_devices.map(&:description).join("\n ")}")
37
+ end
38
+
32
39
  # Install missing runtimes if needed.
33
40
  runtime_helper.install_missing_runtimes(required_devices)
34
41
 
@@ -39,12 +46,7 @@ module Fastlane
39
46
  matched_devices = required_devices
40
47
  .reject { |required_device| required_device.available_device.nil? }
41
48
 
42
- if verbose
43
- UI.message('Matched devices:')
44
- matched_devices.each do |matched_device|
45
- UI.message("\n\t#{matched_device.description}: #{matched_device.available_device.description}")
46
- end
47
- end
49
+ log_matched_devices
48
50
 
49
51
  matched_devices.map!(&:description)
50
52
 
@@ -53,6 +55,18 @@ module Fastlane
53
55
  matched_devices
54
56
  end
55
57
 
58
+ def log_matched_devices
59
+ UI.message('Matched devices:')
60
+ matched_devices.each do |matched_device|
61
+ device_info = ''
62
+ if verbose
63
+ device_info = shell_helper.device_info_by_udid(matched_device.available_device.udid)
64
+ device_info = "\n#{device_info}"
65
+ end
66
+ UI.message(" #{matched_device.description}: #{matched_device.available_device.description}#{device_info}")
67
+ end
68
+ end
69
+
56
70
  def delete_unavailable_devices
57
71
  return unless shell_helper.available_devices_for_runtimes.values.flatten.any?(&:available?)
58
72
 
@@ -68,7 +82,12 @@ module Fastlane
68
82
 
69
83
  return [] if available_devices.nil?
70
84
 
71
- available_devices.detect { |device| device.device_type_identifier == required_device.device_type.identifier }
85
+ # Find the device with the same name as the required device.
86
+ devices_with_same_type = available_devices
87
+ .select { |available_device| available_device.device_type_identifier == required_device.device_type.identifier }
88
+
89
+ devices_with_same_type
90
+ .detect { |available_device| available_device.name == required_device.device_type.name }
72
91
  end
73
92
 
74
93
  def create_missing_devices(required_devices)
@@ -77,12 +96,15 @@ module Fastlane
77
96
  missing_devices = required_devices
78
97
  .select { |required_device| required_device.available_device.nil? }
79
98
 
80
- return if missing_devices.empty?
99
+ if missing_devices.empty?
100
+ UI.message('All required devices are present. Skipping device creation...') if verbose
101
+ return
102
+ end
81
103
 
82
104
  UI.message('Creating missing devices')
83
105
  missing_devices.each do |missing_device|
84
106
  shell_helper.create_device(
85
- missing_device.description,
107
+ missing_device.device_type.name,
86
108
  missing_device.device_type.identifier,
87
109
  missing_device.available_runtime.identifier
88
110
  )
@@ -113,9 +135,11 @@ module Fastlane
113
135
  def required_device_for_device(device)
114
136
  available_device_types = shell_helper.available_device_types
115
137
 
138
+ device_os_version = device[/ \(([\d.]*?)\)$/, 1]
139
+ device_name = device.delete_suffix(" (#{device_os_version})").strip unless device_os_version.nil?
140
+
116
141
  device_type = available_device_types.detect do |available_device_type|
117
- # Avoid matching "iPhone 16" for the "iPhone 16e" device.
118
- "#{device} ".start_with?("#{available_device_type.name} ")
142
+ device_name == available_device_type.name
119
143
  end
120
144
 
121
145
  unless device_type
@@ -126,8 +150,6 @@ module Fastlane
126
150
  product_family = device_type.product_family
127
151
 
128
152
  os_name = PRODUCT_FAMILY_TO_OS_NAME[product_family]
129
- device_os_version = device.delete_prefix(device_type.name).strip
130
- device_os_version = device_os_version[/\(([\d.]*?)\)/, 1]
131
153
 
132
154
  runtime_version = nil
133
155
  unless device_os_version.nil? || device_os_version.empty?
@@ -43,7 +43,10 @@ module Fastlane
43
43
 
44
44
  missing_runtimes = missing_runtimes(needed_runtimes)
45
45
 
46
- return if missing_runtimes.empty?
46
+ if missing_runtimes.empty?
47
+ UI.message('All required runtimes are present. Skipping runtime installation...') if verbose
48
+ return
49
+ end
47
50
 
48
51
  missing_runtimes.each do |missing_runtime|
49
52
  download_and_install_missing_runtime(missing_runtime)
@@ -74,8 +77,24 @@ module Fastlane
74
77
  def available_runtime_matching_needed_runtime?(needed_runtime)
75
78
  matching_runtimes = shell_helper.available_runtimes
76
79
  .select do |available_runtime|
77
- next false if needed_runtime.os_name != available_runtime.platform ||
78
- needed_runtime.product_version != available_runtime.version
80
+ next false if needed_runtime.os_name != available_runtime.platform
81
+
82
+ # If the product version is not equal, check if the first two segments are equal.
83
+ is_product_version_equal = if needed_runtime.product_version == available_runtime.version
84
+ true
85
+ else
86
+ lhs_segments = needed_runtime.product_version.segments
87
+ rhs_segments = available_runtime.version.segments
88
+ if rhs_segments.size == 3
89
+ lhs_segments[0] == rhs_segments[0] && lhs_segments[1] == rhs_segments[1]
90
+ else
91
+ false
92
+ end
93
+ end
94
+ next false unless is_product_version_equal
95
+
96
+ # If the product version is not equal, use the available runtime version.
97
+ needed_runtime.product_version = available_runtime.version
79
98
 
80
99
  needed_runtime.product_build_version = [needed_runtime.product_build_version, available_runtime.build_version].compact.max
81
100
 
@@ -108,17 +127,6 @@ module Fastlane
108
127
  available_runtime
109
128
  end
110
129
 
111
- def install_missing_runtime(missing_runtime, cached_runtime_file)
112
- runtime_name = missing_runtime.runtime_name
113
-
114
- if missing_runtime.product_build_version.nil?
115
- UI.important("Failed to find runtime build version for #{runtime_name}")
116
- return
117
- end
118
-
119
- shell_helper.import_runtime(cached_runtime_file, runtime_name)
120
- end
121
-
122
130
  def download_and_install_missing_runtime(missing_runtime)
123
131
  UI.message("Attempting to install #{missing_runtime.runtime_name} runtime.")
124
132
 
@@ -152,9 +160,15 @@ module Fastlane
152
160
  # shipped with Xcode betas and use the same product version.
153
161
  # E.g. Xcode 26.0 Beta 3 has iOS 26.0 (23A5287e) SDK, but
154
162
  # xcodebuild downloads iphonesimulator_26.0_23A5287g.dmg as latest.
155
- runtime_dmg_search_pattern += missing_runtime.product_build_version.to_s.chop if missing_runtime.product_build_version
163
+ runtime_dmg_search_pattern += missing_runtime.product_build_version.minor_version.to_s if missing_runtime.product_build_version
156
164
  runtime_dmg_search_pattern += '*.dmg'
157
165
 
166
+ if verbose
167
+ UI.message("Searching for #{missing_runtime.runtime_name} runtime image in #{cache_dir} with pattern: #{runtime_dmg_search_pattern}")
168
+ UI.message("Available dmg files: #{Dir.glob("#{cache_dir}/*.dmg")}")
169
+ UI.message("Available files with pattern: #{Dir.glob(runtime_dmg_search_pattern)}")
170
+ end
171
+
158
172
  runtime_file = Dir
159
173
  .glob(runtime_dmg_search_pattern)
160
174
  .max_by { |filename| runtime_build_version_for_filename(filename) }
@@ -10,16 +10,17 @@ module Fastlane
10
10
  UI = ::Fastlane::UI unless defined?(UI)
11
11
 
12
12
  # Proprty verbose
13
- attr_accessor :verbose, :action_context
13
+ attr_accessor :print_command, :print_command_output, :action_context
14
14
 
15
- def initialize(verbose: false, action_context: nil)
16
- self.verbose = verbose
15
+ def initialize(print_command: false, print_command_output: false, action_context: nil)
16
+ self.print_command = print_command
17
+ self.print_command_output = print_command_output
17
18
  self.action_context = action_context
18
19
  end
19
20
 
20
- def sh(command:, print_command: verbose, print_command_output: verbose)
21
+ def sh(command:, print_command: self.print_command, print_command_output: self.print_command_output)
21
22
  if action_context
22
- action_context.sh(command, print_command:, print_command_output:)
23
+ action_context.sh(command, print_command: print_command, print_command_output: print_command_output)
23
24
  else
24
25
  # Fallback for testing or direct usage
25
26
  require 'open3'
@@ -148,11 +149,8 @@ module Fastlane
148
149
  missing_runtime.os_name.shellescape
149
150
  ]
150
151
 
151
- unless missing_runtime.latest?
152
- command << '-buildVersion'
153
- # Prefer the build version if available, otherwise use the product version.
154
- command << (missing_runtime.product_build_version || missing_runtime.product_version.to_s).shellescape
155
- end
152
+ command << '-buildVersion'
153
+ command << missing_runtime.product_version.to_s.shellescape
156
154
 
157
155
  sh(command: command.join(' '), print_command: true, print_command_output: true)
158
156
  end
@@ -167,15 +165,13 @@ module Fastlane
167
165
  end
168
166
  end
169
167
 
170
- # def add_runtime(runtime_dmg_filename, runtime_name)
171
- # UI.message("Adding runtime #{runtime_name}...")
172
- # add_runtime_command = "xcrun simctl runtime add #{runtime_dmg_filename.shellescape}"
173
- # begin
174
- # sh(command: add_runtime_command)
175
- # rescue StandardError => e
176
- # UI.important("Failed to add runtime #{runtime_name} with '#{add_runtime_command}':\n#{e}")
177
- # end
178
- # end
168
+ def device_info_by_udid(udid)
169
+ sh(
170
+ command: "xcrun simctl list devices --json | jq '.devices | to_entries[].value[] | select(.udid==\"#{udid.shellescape}\")'",
171
+ print_command: false,
172
+ print_command_output: false
173
+ )
174
+ end
179
175
  end
180
176
  end
181
177
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Fastlane
4
4
  module CreateSimulatorDevices
5
- VERSION = '0.0.4'
5
+ VERSION = '0.0.6'
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.4
4
+ version: 0.0.6
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-07-17 00:00:00.000000000 Z
11
+ date: 2025-08-29 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email: developers@setapp.com