emerge 0.5.0 → 0.6.0
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 +4 -4
- data/lib/commands/build_distribution/download_and_install.rb +45 -13
- data/lib/emerge_cli.rb +4 -0
- data/lib/utils/environment.rb +7 -0
- data/lib/utils/xcode_device_manager.rb +158 -0
- data/lib/utils/xcode_physical_device.rb +108 -0
- data/lib/utils/xcode_simulator.rb +114 -0
- data/lib/version.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0cce2cb7764c86a70569537aab8d02bd3136df5c4b25b2421164bf0e28256623
|
4
|
+
data.tar.gz: e4cce6ab186fd05a9910acc99cb279d60e7799f36273f45d6496523e785ede62
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5ebaf9ef828373305f07c597fb2968d2e046689d5954226210ba9ca164c919b146bc208c83adcd77acc77d7a535feeeb31c7cc0718944c05fb7990cbc7e1beee
|
7
|
+
data.tar.gz: 46e65589c521670cff6ff14106e96c33d9fb2aa0d8293b5521878f1e97a2aef59516a193a08c75b4371c1d83af15b2b98f56e8245bdd4c83c72c0565c3c6130a
|
@@ -2,6 +2,7 @@ require 'dry/cli'
|
|
2
2
|
require 'cfpropertylist'
|
3
3
|
require 'zip'
|
4
4
|
require 'rbconfig'
|
5
|
+
require 'tmpdir'
|
5
6
|
|
6
7
|
module EmergeCLI
|
7
8
|
module Commands
|
@@ -13,7 +14,9 @@ module EmergeCLI
|
|
13
14
|
desc: 'API token for authentication, defaults to ENV[EMERGE_API_TOKEN]'
|
14
15
|
option :build_id, type: :string, required: true, desc: 'Build ID to download'
|
15
16
|
option :install, type: :boolean, default: true, required: false, desc: 'Install the build on the device'
|
16
|
-
option :device_id, type: :string,
|
17
|
+
option :device_id, type: :string, desc: 'Specific device ID to target'
|
18
|
+
option :device_type, type: :string, enum: %w[virtual physical any], default: 'any',
|
19
|
+
desc: 'Type of device to target (virtual/physical/any)'
|
17
20
|
option :output, type: :string, required: false, desc: 'Output path for the downloaded build'
|
18
21
|
|
19
22
|
def initialize(network: nil)
|
@@ -30,6 +33,9 @@ module EmergeCLI
|
|
30
33
|
|
31
34
|
raise 'Build ID is required' unless @options[:build_id]
|
32
35
|
|
36
|
+
output_name = nil
|
37
|
+
app_id = nil
|
38
|
+
|
33
39
|
begin
|
34
40
|
@network ||= EmergeCLI::Network.new(api_token:)
|
35
41
|
|
@@ -39,24 +45,32 @@ module EmergeCLI
|
|
39
45
|
|
40
46
|
platform = response['platform']
|
41
47
|
download_url = response['downloadUrl']
|
48
|
+
app_id = response['appId']
|
42
49
|
|
43
50
|
extension = platform == 'ios' ? 'ipa' : 'apk'
|
44
51
|
Logger.info 'Downloading build...'
|
45
52
|
output_name = @options[:output] || "#{@options[:build_id]}.#{extension}"
|
46
53
|
`curl --progress-bar -L '#{download_url}' -o #{output_name} `
|
47
54
|
Logger.info "✅ Build downloaded to #{output_name}"
|
48
|
-
|
49
|
-
if @options[:install]
|
50
|
-
install_ios_build(output_name) if platform == 'ios'
|
51
|
-
install_android_build(output_name) if platform == 'android'
|
52
|
-
end
|
53
55
|
rescue StandardError => e
|
54
|
-
Logger.error "Failed to download build: #{e.message}"
|
55
|
-
Logger.error 'Check your parameters and try again'
|
56
|
+
Logger.error "❌ Failed to download build: #{e.message}"
|
56
57
|
raise e
|
57
58
|
ensure
|
58
59
|
@network&.close
|
59
60
|
end
|
61
|
+
|
62
|
+
begin
|
63
|
+
if @options[:install] && !output_name.nil?
|
64
|
+
if platform == 'ios'
|
65
|
+
install_ios_build(output_name, app_id)
|
66
|
+
elsif platform == 'android'
|
67
|
+
install_android_build(output_name)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
rescue StandardError => e
|
71
|
+
Logger.error "❌ Failed to install build: #{e.message}"
|
72
|
+
raise e
|
73
|
+
end
|
60
74
|
end
|
61
75
|
end
|
62
76
|
|
@@ -86,12 +100,30 @@ module EmergeCLI
|
|
86
100
|
end
|
87
101
|
end
|
88
102
|
|
89
|
-
def install_ios_build(build_path)
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
103
|
+
def install_ios_build(build_path, app_id)
|
104
|
+
device_type = case @options[:device_type]
|
105
|
+
when 'simulator'
|
106
|
+
XcodeDeviceManager::DeviceType::VIRTUAL
|
107
|
+
when 'physical'
|
108
|
+
XcodeDeviceManager::DeviceType::PHYSICAL
|
109
|
+
else
|
110
|
+
XcodeDeviceManager::DeviceType::ANY
|
111
|
+
end
|
112
|
+
|
113
|
+
device_manager = XcodeDeviceManager.new
|
114
|
+
device = if @options[:device_id]
|
115
|
+
device_manager.find_device_by_id(@options[:device_id])
|
116
|
+
else
|
117
|
+
device_manager.find_device_by_type(device_type, build_path)
|
118
|
+
end
|
119
|
+
|
120
|
+
Logger.info "Installing build on #{device.device_id}"
|
121
|
+
device.install_app(build_path)
|
94
122
|
Logger.info '✅ Build installed'
|
123
|
+
|
124
|
+
Logger.info "Launching app #{app_id}..."
|
125
|
+
device.launch_app(app_id)
|
126
|
+
Logger.info '✅ Build launched'
|
95
127
|
end
|
96
128
|
|
97
129
|
def install_android_build(build_path)
|
data/lib/emerge_cli.rb
CHANGED
@@ -24,6 +24,7 @@ require_relative 'commands/autofixes/exported_symbols'
|
|
24
24
|
require_relative 'reaper/ast_parser'
|
25
25
|
require_relative 'reaper/code_deleter'
|
26
26
|
|
27
|
+
require_relative 'utils/environment'
|
27
28
|
require_relative 'utils/git_info_provider'
|
28
29
|
require_relative 'utils/git_result'
|
29
30
|
require_relative 'utils/github'
|
@@ -34,6 +35,9 @@ require_relative 'utils/profiler'
|
|
34
35
|
require_relative 'utils/project_detector'
|
35
36
|
require_relative 'utils/macho_parser'
|
36
37
|
require_relative 'utils/version_check'
|
38
|
+
require_relative 'utils/xcode_device_manager'
|
39
|
+
require_relative 'utils/xcode_simulator'
|
40
|
+
require_relative 'utils/xcode_physical_device'
|
37
41
|
|
38
42
|
require 'dry/cli'
|
39
43
|
|
@@ -0,0 +1,158 @@
|
|
1
|
+
require 'json'
|
2
|
+
require_relative 'xcode_simulator'
|
3
|
+
require 'zip'
|
4
|
+
require 'cfpropertylist'
|
5
|
+
|
6
|
+
module EmergeCLI
|
7
|
+
class XcodeDeviceManager
|
8
|
+
class DeviceType
|
9
|
+
VIRTUAL = :virtual
|
10
|
+
PHYSICAL = :physical
|
11
|
+
ANY = :any
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(environment: Environment.new)
|
15
|
+
@environment = environment
|
16
|
+
end
|
17
|
+
|
18
|
+
class << self
|
19
|
+
def get_supported_platforms(ipa_path)
|
20
|
+
return [] unless ipa_path&.end_with?('.ipa')
|
21
|
+
|
22
|
+
Zip::File.open(ipa_path) do |zip_file|
|
23
|
+
app_entry = zip_file.glob('**/*.app/').first ||
|
24
|
+
zip_file.glob('**/*.app').first ||
|
25
|
+
zip_file.find { |entry| entry.name.end_with?('.app/') || entry.name.end_with?('.app') }
|
26
|
+
|
27
|
+
raise 'No .app found in .ipa file' unless app_entry
|
28
|
+
|
29
|
+
app_dir = app_entry.name.end_with?('/') ? app_entry.name.chomp('/') : app_entry.name
|
30
|
+
info_plist_path = "#{app_dir}/Info.plist"
|
31
|
+
info_plist_entry = zip_file.find_entry(info_plist_path)
|
32
|
+
raise 'Info.plist not found in app bundle' unless info_plist_entry
|
33
|
+
|
34
|
+
info_plist_content = info_plist_entry.get_input_stream.read
|
35
|
+
plist = CFPropertyList::List.new(data: info_plist_content)
|
36
|
+
info_plist = CFPropertyList.native_types(plist.value)
|
37
|
+
|
38
|
+
info_plist['CFBundleSupportedPlatforms'] || []
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def find_device_by_id(device_id)
|
44
|
+
Logger.debug "Looking for device with ID: #{device_id}"
|
45
|
+
devices_json = execute_command('xcrun xcdevice list')
|
46
|
+
devices_data = JSON.parse(devices_json)
|
47
|
+
|
48
|
+
found_device = devices_data.find { |device| device['identifier'] == device_id }
|
49
|
+
raise "No device found with ID: #{device_id}" unless found_device
|
50
|
+
|
51
|
+
device_type = found_device['simulator'] ? 'simulator' : 'physical'
|
52
|
+
Logger.info "✅ Found device: #{found_device['name']} " \
|
53
|
+
"(#{found_device['identifier']}, #{device_type})"
|
54
|
+
if found_device['simulator']
|
55
|
+
XcodeSimulator.new(found_device['identifier'])
|
56
|
+
else
|
57
|
+
XcodePhysicalDevice.new(found_device['identifier'])
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def find_device_by_type(device_type, ipa_path)
|
62
|
+
case device_type
|
63
|
+
when DeviceType::VIRTUAL
|
64
|
+
find_and_boot_most_recently_used_simulator
|
65
|
+
when DeviceType::PHYSICAL
|
66
|
+
find_connected_device
|
67
|
+
when DeviceType::ANY
|
68
|
+
# Check supported platforms in Info.plist to make intelligent choice
|
69
|
+
supported_platforms = self.class.get_supported_platforms(ipa_path)
|
70
|
+
Logger.debug "Build supports platforms: #{supported_platforms.join(', ')}"
|
71
|
+
|
72
|
+
if supported_platforms.include?('iPhoneOS')
|
73
|
+
device = find_connected_device
|
74
|
+
return device if device
|
75
|
+
|
76
|
+
# Only fall back to simulator if it's also supported
|
77
|
+
unless supported_platforms.include?('iPhoneSimulator')
|
78
|
+
raise 'Build only supports physical devices, but no device is connected'
|
79
|
+
end
|
80
|
+
Logger.info 'No physical device found, falling back to simulator since build supports both'
|
81
|
+
find_and_boot_most_recently_used_simulator
|
82
|
+
|
83
|
+
elsif supported_platforms.include?('iPhoneSimulator')
|
84
|
+
find_and_boot_most_recently_used_simulator
|
85
|
+
else
|
86
|
+
raise "Build doesn't support either physical devices or simulators"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def execute_command(command)
|
94
|
+
@environment.execute_command(command)
|
95
|
+
end
|
96
|
+
|
97
|
+
def find_connected_device
|
98
|
+
Logger.info 'Finding connected device...'
|
99
|
+
devices_json = execute_command('xcrun xcdevice list')
|
100
|
+
Logger.debug "Device list output: #{devices_json}"
|
101
|
+
|
102
|
+
devices_data = JSON.parse(devices_json)
|
103
|
+
physical_devices = devices_data
|
104
|
+
.select do |device|
|
105
|
+
device['simulator'] == false &&
|
106
|
+
device['ignored'] == false &&
|
107
|
+
device['available'] == true &&
|
108
|
+
device['platform'] == 'com.apple.platform.iphoneos'
|
109
|
+
end
|
110
|
+
|
111
|
+
Logger.debug "Found physical devices: #{physical_devices}"
|
112
|
+
|
113
|
+
if physical_devices.empty?
|
114
|
+
Logger.info 'No physical connected device found'
|
115
|
+
return nil
|
116
|
+
end
|
117
|
+
|
118
|
+
device = physical_devices.first
|
119
|
+
Logger.info "Found connected physical device: #{device['name']} (#{device['identifier']})"
|
120
|
+
XcodePhysicalDevice.new(device['identifier'])
|
121
|
+
end
|
122
|
+
|
123
|
+
def find_and_boot_most_recently_used_simulator
|
124
|
+
Logger.info 'Finding and booting most recently used simulator...'
|
125
|
+
simulators_json = execute_command('xcrun simctl list devices --json')
|
126
|
+
Logger.debug "Simulators JSON: #{simulators_json}"
|
127
|
+
|
128
|
+
simulators_data = JSON.parse(simulators_json)
|
129
|
+
|
130
|
+
simulators = simulators_data['devices'].flat_map do |runtime, devices|
|
131
|
+
next [] unless runtime.include?('iOS') # Only include iOS devices
|
132
|
+
|
133
|
+
devices.select do |device|
|
134
|
+
(device['name'].start_with?('iPhone', 'iPad') &&
|
135
|
+
device['isAvailable'] &&
|
136
|
+
!device['isDeleted'])
|
137
|
+
end.map do |device|
|
138
|
+
version = runtime.match(/iOS-(\d+)-(\d+)/)&.captures&.join('.').to_f
|
139
|
+
last_booted = device['lastBootedAt'] ? Time.parse(device['lastBootedAt']) : Time.at(0)
|
140
|
+
[device['udid'], device['state'], version, last_booted]
|
141
|
+
end
|
142
|
+
end.sort_by { |_, _, _, last_booted| last_booted }.reverse
|
143
|
+
|
144
|
+
Logger.debug "Simulators: #{simulators}"
|
145
|
+
|
146
|
+
raise 'No available simulator found' unless simulators.any?
|
147
|
+
|
148
|
+
simulator_id, simulator_state, version, last_booted = simulators.first
|
149
|
+
version_str = version.zero? ? '' : " (#{version})"
|
150
|
+
last_booted_str = last_booted == Time.at(0) ? 'never' : last_booted.strftime('%Y-%m-%d %H:%M:%S')
|
151
|
+
Logger.info "Found simulator #{simulator_id}#{version_str} (#{simulator_state}, last booted: #{last_booted_str})"
|
152
|
+
|
153
|
+
simulator = XcodeSimulator.new(simulator_id, environment: @environment)
|
154
|
+
simulator.boot unless simulator_state == 'Booted'
|
155
|
+
simulator
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'English'
|
2
|
+
require 'timeout'
|
3
|
+
require 'zip'
|
4
|
+
require 'cfpropertylist'
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
module EmergeCLI
|
8
|
+
class XcodePhysicalDevice
|
9
|
+
attr_reader :device_id
|
10
|
+
|
11
|
+
def initialize(device_id)
|
12
|
+
@device_id = device_id
|
13
|
+
end
|
14
|
+
|
15
|
+
def install_app(ipa_path)
|
16
|
+
raise "Non-IPA file provided: #{ipa_path}" unless ipa_path.end_with?('.ipa')
|
17
|
+
|
18
|
+
Logger.info "Installing app to device #{@device_id}..."
|
19
|
+
|
20
|
+
begin
|
21
|
+
# Set a timeout since I've noticed xcrun devicectl can occasionally hang for invalid apps
|
22
|
+
Timeout.timeout(60) do
|
23
|
+
command = "xcrun devicectl device install app --device #{@device_id} \"#{ipa_path}\""
|
24
|
+
Logger.debug "Running command: #{command}"
|
25
|
+
|
26
|
+
output = `#{command} 2>&1`
|
27
|
+
Logger.debug "Install command output: #{output}"
|
28
|
+
|
29
|
+
if output.include?('ERROR:') || output.include?('error:')
|
30
|
+
if output.include?('This provisioning profile cannot be installed on this device')
|
31
|
+
bundle_id = extract_bundle_id_from_error(output)
|
32
|
+
raise "Failed to install app: The provisioning profile for #{bundle_id} is not " \
|
33
|
+
"valid for this device. Make sure the device's UDID is included in the " \
|
34
|
+
'provisioning profile.'
|
35
|
+
elsif output.include?('Unable to Install')
|
36
|
+
error_message = output.match(/Unable to Install.*\n.*NSLocalizedRecoverySuggestion = ([^\n]+)/)&.[](1)
|
37
|
+
check_device_compatibility(ipa_path)
|
38
|
+
raise "Failed to install app: #{error_message || 'Unknown error'}"
|
39
|
+
else
|
40
|
+
check_device_compatibility(ipa_path)
|
41
|
+
raise "Failed to install app: #{output}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
success = $CHILD_STATUS.success?
|
46
|
+
unless success
|
47
|
+
check_device_compatibility(ipa_path)
|
48
|
+
raise "Installation failed with exit code #{$CHILD_STATUS.exitstatus}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
rescue Timeout::Error
|
52
|
+
raise 'Installation timed out after 30 seconds. The device might be locked or ' \
|
53
|
+
'installation might be stuck. Try unlocking the device and trying again.'
|
54
|
+
end
|
55
|
+
|
56
|
+
true
|
57
|
+
end
|
58
|
+
|
59
|
+
def launch_app(bundle_id)
|
60
|
+
command = "xcrun devicectl device process launch --device #{@device_id} #{bundle_id}"
|
61
|
+
Logger.debug "Running command: #{command}"
|
62
|
+
|
63
|
+
begin
|
64
|
+
Timeout.timeout(30) do
|
65
|
+
output = `#{command} 2>&1`
|
66
|
+
success = $CHILD_STATUS.success?
|
67
|
+
|
68
|
+
unless success
|
69
|
+
Logger.debug "Launch command output: #{output}"
|
70
|
+
if output.include?('The operation couldn\'t be completed. Application is restricted')
|
71
|
+
raise 'Failed to launch app: The app is restricted. Make sure the device is ' \
|
72
|
+
'unlocked and the app is allowed to run.'
|
73
|
+
elsif output.include?('The operation couldn\'t be completed. Unable to launch')
|
74
|
+
raise 'Failed to launch app: Unable to launch. The app might be in a bad state - ' \
|
75
|
+
'try uninstalling and reinstalling.'
|
76
|
+
else
|
77
|
+
raise "Failed to launch app #{bundle_id} on device: #{output}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
rescue Timeout::Error
|
82
|
+
raise 'Launch timed out after 30 seconds. The device might be locked. ' \
|
83
|
+
'Try unlocking the device and trying again.'
|
84
|
+
end
|
85
|
+
|
86
|
+
true
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def check_device_compatibility(ipa_path)
|
92
|
+
supported_platforms = XcodeDeviceManager.get_supported_platforms(ipa_path)
|
93
|
+
Logger.debug "Supported platforms: #{supported_platforms.join(', ')}"
|
94
|
+
|
95
|
+
unless supported_platforms.include?('iPhoneOS')
|
96
|
+
raise 'This build is not compatible with physical devices. Please use a simulator ' \
|
97
|
+
'or make your build compatible with physical devices.'
|
98
|
+
end
|
99
|
+
|
100
|
+
Logger.debug 'Build is compatible with physical devices'
|
101
|
+
end
|
102
|
+
|
103
|
+
def extract_bundle_id_from_error(output)
|
104
|
+
# Extract bundle ID from error message like "...profile for com.emerge.hn.Hacker-News :"
|
105
|
+
output.match(/profile for ([\w\.-]+) :/)&.[](1) || 'unknown bundle ID'
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'English'
|
2
|
+
require 'zip'
|
3
|
+
require 'cfpropertylist'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
module EmergeCLI
|
7
|
+
class XcodeSimulator
|
8
|
+
attr_reader :device_id
|
9
|
+
|
10
|
+
def initialize(device_id, environment: Environment.new)
|
11
|
+
@device_id = device_id
|
12
|
+
@environment = environment
|
13
|
+
end
|
14
|
+
|
15
|
+
def boot
|
16
|
+
Logger.info "Booting simulator #{@device_id}..."
|
17
|
+
output = @environment.execute_command("xcrun simctl boot #{@device_id}")
|
18
|
+
raise 'Failed to boot simulator' if output.include?('error') || output.include?('failed')
|
19
|
+
end
|
20
|
+
|
21
|
+
def install_app(ipa_path)
|
22
|
+
raise "Non-IPA file provided: #{ipa_path}" unless ipa_path.end_with?('.ipa')
|
23
|
+
|
24
|
+
Dir.mktmpdir do |tmp_dir|
|
25
|
+
Logger.debug "Extracting .app from .ipa in temporary directory: #{tmp_dir}"
|
26
|
+
|
27
|
+
Zip::File.open(ipa_path) do |zip_file|
|
28
|
+
# Debug: List all entries to see what's in the IPA
|
29
|
+
Logger.debug 'IPA contents:'
|
30
|
+
zip_file.each do |entry|
|
31
|
+
Logger.debug " #{entry.name}"
|
32
|
+
end
|
33
|
+
|
34
|
+
# Try different patterns to find the .app directory
|
35
|
+
app_entry = zip_file.glob('**/*.app/').first ||
|
36
|
+
zip_file.glob('**/*.app').first ||
|
37
|
+
zip_file.find { |entry| entry.name.end_with?('.app/') || entry.name.end_with?('.app') }
|
38
|
+
|
39
|
+
raise 'No .app found in .ipa file' unless app_entry
|
40
|
+
Logger.debug "Found app entry: #{app_entry.name}"
|
41
|
+
|
42
|
+
# Extract the .app directory and its contents
|
43
|
+
app_dir = app_entry.name.end_with?('/') ? app_entry.name.chomp('/') : app_entry.name
|
44
|
+
pattern = "#{File.dirname(app_dir)}/#{File.basename(app_dir)}/**/*"
|
45
|
+
Logger.debug "Using glob pattern: #{pattern}"
|
46
|
+
|
47
|
+
zip_file.glob(pattern).each do |entry|
|
48
|
+
entry_path = File.join(tmp_dir, entry.name)
|
49
|
+
FileUtils.mkdir_p(File.dirname(entry_path))
|
50
|
+
zip_file.extract(entry, entry_path) unless File.exist?(entry_path)
|
51
|
+
end
|
52
|
+
|
53
|
+
extracted_app = Dir.glob(File.join(tmp_dir, '**/*.app')).first
|
54
|
+
raise 'Failed to extract .app from .ipa' unless extracted_app
|
55
|
+
Logger.debug "Extracted app at: #{extracted_app}"
|
56
|
+
|
57
|
+
install_extracted_app(extracted_app)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def launch_app(bundle_id)
|
63
|
+
command = "xcrun simctl launch #{@device_id} #{bundle_id}"
|
64
|
+
Logger.debug "Running command: #{command}"
|
65
|
+
|
66
|
+
output = `#{command} 2>&1`
|
67
|
+
success = $CHILD_STATUS.success?
|
68
|
+
|
69
|
+
unless success
|
70
|
+
Logger.debug "Launch command output: #{output}"
|
71
|
+
raise "Failed to launch app #{bundle_id} on simulator"
|
72
|
+
end
|
73
|
+
|
74
|
+
true
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def install_extracted_app(app_path)
|
80
|
+
command = "xcrun simctl install #{@device_id} \"#{app_path}\""
|
81
|
+
Logger.debug "Running command: #{command}"
|
82
|
+
|
83
|
+
output = `#{command} 2>&1`
|
84
|
+
success = $CHILD_STATUS.success?
|
85
|
+
|
86
|
+
return if success
|
87
|
+
Logger.debug "Install command output: #{output}"
|
88
|
+
check_simulator_compatibility(app_path)
|
89
|
+
raise "Failed to install build on simulator #{@device_id}"
|
90
|
+
end
|
91
|
+
|
92
|
+
def check_simulator_compatibility(app_path)
|
93
|
+
supported_platforms = if app_path.end_with?('.ipa')
|
94
|
+
XcodeDeviceManager.get_supported_platforms(app_path)
|
95
|
+
else
|
96
|
+
info_plist_path = File.join(app_path, 'Info.plist')
|
97
|
+
raise 'Info.plist not found in app bundle' unless File.exist?(info_plist_path)
|
98
|
+
|
99
|
+
plist = CFPropertyList::List.new(file: info_plist_path)
|
100
|
+
info_plist = CFPropertyList.native_types(plist.value)
|
101
|
+
info_plist['CFBundleSupportedPlatforms'] || []
|
102
|
+
end
|
103
|
+
|
104
|
+
Logger.debug "Supported platforms: #{supported_platforms.join(', ')}"
|
105
|
+
|
106
|
+
unless supported_platforms.include?('iPhoneSimulator')
|
107
|
+
raise 'This build is not compatible with simulators. ' \
|
108
|
+
'Please use a real device or make your build compatible with simulators.'
|
109
|
+
end
|
110
|
+
|
111
|
+
Logger.debug 'Build is compatible with simulators'
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
data/lib/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: emerge
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Emerge Tools
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-01-
|
11
|
+
date: 2025-01-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: async-http
|
@@ -204,6 +204,7 @@ files:
|
|
204
204
|
- lib/emerge_cli.rb
|
205
205
|
- lib/reaper/ast_parser.rb
|
206
206
|
- lib/reaper/code_deleter.rb
|
207
|
+
- lib/utils/environment.rb
|
207
208
|
- lib/utils/git.rb
|
208
209
|
- lib/utils/git_info_provider.rb
|
209
210
|
- lib/utils/git_result.rb
|
@@ -214,6 +215,9 @@ files:
|
|
214
215
|
- lib/utils/profiler.rb
|
215
216
|
- lib/utils/project_detector.rb
|
216
217
|
- lib/utils/version_check.rb
|
218
|
+
- lib/utils/xcode_device_manager.rb
|
219
|
+
- lib/utils/xcode_physical_device.rb
|
220
|
+
- lib/utils/xcode_simulator.rb
|
217
221
|
- lib/version.rb
|
218
222
|
- parsers/libtree-sitter-java-darwin-arm64.dylib
|
219
223
|
- parsers/libtree-sitter-java-linux-x86_64.so
|