emerge 0.2.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +28 -3
- data/lib/commands/global_options.rb +2 -0
- data/lib/commands/order_files/download_order_files.rb +77 -0
- data/lib/commands/order_files/validate_linkmaps.rb +55 -0
- data/lib/commands/reaper/reaper.rb +201 -0
- data/lib/commands/snapshots/validate_app.rb +64 -0
- data/lib/emerge_cli.rb +18 -2
- data/lib/reaper/ast_parser.rb +188 -3
- data/lib/reaper/code_deleter.rb +263 -0
- data/lib/utils/git.rb +13 -1
- data/lib/utils/macho_parser.rb +325 -0
- data/lib/utils/network.rb +20 -13
- data/lib/utils/version_check.rb +32 -0
- data/lib/version.rb +1 -1
- data/parsers/libtree-sitter-java-darwin-arm64.dylib +0 -0
- data/parsers/libtree-sitter-java-linux-x86_64.so +0 -0
- data/parsers/libtree-sitter-kotlin-darwin-arm64.dylib +0 -0
- data/parsers/libtree-sitter-kotlin-linux-x86_64.so +0 -0
- data/parsers/libtree-sitter-swift-darwin-arm64.dylib +0 -0
- data/parsers/libtree-sitter-swift-linux-x86_64.so +0 -0
- metadata +31 -12
@@ -0,0 +1,325 @@
|
|
1
|
+
require 'macho'
|
2
|
+
|
3
|
+
module EmergeCLI
|
4
|
+
class MachOParser
|
5
|
+
TYPE_METADATA_KIND_MASK = 0x7 << 3
|
6
|
+
TYPE_METADATA_KIND_SHIFT = 3
|
7
|
+
|
8
|
+
# Bind Codes
|
9
|
+
BIND_OPCODE_MASK = 0xF0
|
10
|
+
BIND_IMMEDIATE_MASK = 0x0F
|
11
|
+
BIND_OPCODE_DONE = 0x00
|
12
|
+
BIND_OPCODE_SET_DYLIB_ORDINAL_IMM = 0x10
|
13
|
+
BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB = 0x20
|
14
|
+
BIND_OPCODE_SET_DYLIB_SPECIAL_IMM = 0x30
|
15
|
+
BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM = 0x40
|
16
|
+
BIND_OPCODE_SET_TYPE_IMM = 0x50
|
17
|
+
BIND_OPCODE_SET_ADDEND_SLEB = 0x60
|
18
|
+
BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB = 0x70
|
19
|
+
BIND_OPCODE_ADD_ADDR_ULEB = 0x80
|
20
|
+
BIND_OPCODE_DO_BIND = 0x90
|
21
|
+
BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB = 0xA0
|
22
|
+
BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED = 0xB0
|
23
|
+
BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB = 0xC0
|
24
|
+
|
25
|
+
UINT64_SIZE = 8
|
26
|
+
UINT64_MAX_VALUE = 0xFFFFFFFFFFFFFFFF
|
27
|
+
|
28
|
+
def load_binary(binary_path)
|
29
|
+
@macho_file = MachO::MachOFile.new(binary_path)
|
30
|
+
@binary_data = File.binread(binary_path)
|
31
|
+
end
|
32
|
+
|
33
|
+
def read_linkedit_data_command
|
34
|
+
chained_fixups_command = nil
|
35
|
+
@macho_file.load_commands.each do |lc|
|
36
|
+
chained_fixups_command = lc if lc.type == :LC_DYLD_CHAINED_FIXUPS
|
37
|
+
end
|
38
|
+
|
39
|
+
if chained_fixups_command.nil?
|
40
|
+
Logger.debug 'No LC_DYLD_CHAINED_FIXUPS found'
|
41
|
+
return false, []
|
42
|
+
end
|
43
|
+
|
44
|
+
# linkedit_data_command
|
45
|
+
_, _, dataoff, datasize = @binary_data[chained_fixups_command.offset, 16].unpack('L<L<L<L<')
|
46
|
+
|
47
|
+
header = @binary_data[dataoff, datasize].unpack('L<L<L<L<L<L<L<')
|
48
|
+
# dyld_chained_fixups_header
|
49
|
+
_, _, imports_offset, symbols_offset, imports_count,
|
50
|
+
imports_format, = header
|
51
|
+
|
52
|
+
imports_start = dataoff + imports_offset
|
53
|
+
symbols_start = dataoff + symbols_offset
|
54
|
+
|
55
|
+
imported_symbols = []
|
56
|
+
|
57
|
+
import_size, name_offset_proc =
|
58
|
+
case imports_format
|
59
|
+
when 1, nil # DYLD_CHAINED_IMPORT
|
60
|
+
[4, ->(ptr) { ptr.unpack1('L<') >> 9 }]
|
61
|
+
when 2 # DYLD_CHAINED_IMPORT_ADDEND
|
62
|
+
[8, ->(ptr) { ptr.unpack1('L<') >> 9 }]
|
63
|
+
when 3 # DYLD_CHAINED_IMPORT_ADDEND64
|
64
|
+
[16, ->(ptr) { ptr.unpack1('Q<') >> 32 }]
|
65
|
+
end
|
66
|
+
|
67
|
+
# Extract imported symbol names
|
68
|
+
imports_count.times do |i|
|
69
|
+
import_offset = imports_start + (i * import_size)
|
70
|
+
name_offset = name_offset_proc.call(@binary_data[import_offset, import_size])
|
71
|
+
name_start = symbols_start + name_offset
|
72
|
+
name = read_null_terminated_string(@binary_data[name_start..])
|
73
|
+
imported_symbols << name
|
74
|
+
end
|
75
|
+
|
76
|
+
[true, imported_symbols]
|
77
|
+
end
|
78
|
+
|
79
|
+
def read_dyld_info_only_command
|
80
|
+
dyld_info_only_command = nil
|
81
|
+
@macho_file.load_commands.each do |lc|
|
82
|
+
dyld_info_only_command = lc if lc.type == :LC_DYLD_INFO_ONLY
|
83
|
+
end
|
84
|
+
|
85
|
+
if dyld_info_only_command.nil?
|
86
|
+
Logger.debug 'No LC_DYLD_INFO_ONLY found'
|
87
|
+
return []
|
88
|
+
end
|
89
|
+
|
90
|
+
bound_symbols = []
|
91
|
+
start_address = dyld_info_only_command.bind_off
|
92
|
+
end_address = dyld_info_only_command.bind_off + dyld_info_only_command.bind_size
|
93
|
+
current_address = start_address
|
94
|
+
|
95
|
+
current_symbol = BoundSymbol.new(segment_offset: 0, library: nil, offset: 0, symbol: '')
|
96
|
+
while current_address < end_address
|
97
|
+
results, current_address, current_symbol = read_next_symbol(@binary_data, current_address, end_address,
|
98
|
+
current_symbol)
|
99
|
+
|
100
|
+
# Dup items to avoid pointer issues
|
101
|
+
results.each do |res|
|
102
|
+
bound_symbols << res.dup
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Filter only swift symbols starting with _$s
|
107
|
+
swift_symbols = bound_symbols.select { |bound_symbol| bound_symbol.symbol.start_with?('_$s') }
|
108
|
+
|
109
|
+
load_commands = @macho_file.load_commands.select { |lc| lc.type == :LC_SEGMENT_64 || lc.type == :LC_SEGMENT } # rubocop:disable Naming/VariableNumber
|
110
|
+
|
111
|
+
swift_symbols.each do |swift_symbol|
|
112
|
+
swift_symbol.address = load_commands[swift_symbol.segment_offset].vmaddr + swift_symbol.offset
|
113
|
+
end
|
114
|
+
|
115
|
+
swift_symbols
|
116
|
+
end
|
117
|
+
|
118
|
+
def find_protocols_in_swift_proto(use_chained_fixups, imported_symbols, bound_symbols, search_symbols)
|
119
|
+
found_section = nil
|
120
|
+
@macho_file.segments.each do |segment|
|
121
|
+
segment.sections.each do |section|
|
122
|
+
if section.segname.strip == '__TEXT' && section.sectname.strip == '__swift5_proto'
|
123
|
+
found_section = section
|
124
|
+
break
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
unless found_section
|
130
|
+
Logger.error 'The __swift5_proto section was not found.'
|
131
|
+
return false
|
132
|
+
end
|
133
|
+
|
134
|
+
start = found_section.offset
|
135
|
+
size = found_section.size
|
136
|
+
offsets_list = parse_list(@binary_data, start, size)
|
137
|
+
|
138
|
+
offsets_list.each do |relative_offset, offset_start|
|
139
|
+
type_file_address = offset_start + relative_offset
|
140
|
+
if type_file_address <= 0 || type_file_address >= @binary_data.size
|
141
|
+
Logger.error 'Invalid protocol conformance offset'
|
142
|
+
next
|
143
|
+
end
|
144
|
+
|
145
|
+
# ProtocolConformanceDescriptor -> ProtocolDescriptor
|
146
|
+
protocol_descriptor = read_little_endian_signed_integer(@binary_data, type_file_address)
|
147
|
+
|
148
|
+
# # ProtocolConformanceDescriptor -> ConformanceFlags
|
149
|
+
conformance_flags = read_little_endian_signed_integer(@binary_data, type_file_address + 12)
|
150
|
+
kind = (conformance_flags & TYPE_METADATA_KIND_MASK) >> TYPE_METADATA_KIND_SHIFT
|
151
|
+
|
152
|
+
next unless kind == 0
|
153
|
+
|
154
|
+
indirect_relative_offset = get_indirect_relative_offset(type_file_address, protocol_descriptor)
|
155
|
+
|
156
|
+
bound_symbol = bound_symbols.find { |symbol| symbol.address == indirect_relative_offset }
|
157
|
+
if bound_symbol
|
158
|
+
return true if search_symbols.include?(bound_symbol.symbol)
|
159
|
+
elsif use_chained_fixups
|
160
|
+
descriptor_offset = protocol_descriptor & ~1
|
161
|
+
jump_ptr = type_file_address + descriptor_offset
|
162
|
+
|
163
|
+
address = @binary_data[jump_ptr, 4].unpack1('I<')
|
164
|
+
symbol_name = imported_symbols[address]
|
165
|
+
return true if search_symbols.include?(symbol_name)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
false
|
169
|
+
end
|
170
|
+
|
171
|
+
private
|
172
|
+
|
173
|
+
def read_next_symbol(binary_data, current_address, end_address, current_symbol)
|
174
|
+
while current_address < end_address
|
175
|
+
first_byte = read_byte(binary_data, current_address)
|
176
|
+
current_address += 1
|
177
|
+
immediate = first_byte & BIND_IMMEDIATE_MASK
|
178
|
+
opcode = first_byte & BIND_OPCODE_MASK
|
179
|
+
|
180
|
+
case opcode
|
181
|
+
when BIND_OPCODE_DONE
|
182
|
+
result = current_symbol.dup
|
183
|
+
current_symbol.segment_offset = 0
|
184
|
+
current_symbol.library = 0
|
185
|
+
current_symbol.offset = 0
|
186
|
+
current_symbol.symbol = ''
|
187
|
+
return [result], current_address, current_symbol
|
188
|
+
when BIND_OPCODE_SET_DYLIB_ORDINAL_IMM
|
189
|
+
current_symbol.library = [immediate].pack('L').unpack1('L')
|
190
|
+
when BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
|
191
|
+
current_symbol.symbol = read_null_terminated_string(binary_data[current_address..])
|
192
|
+
# Increase current pointer
|
193
|
+
current_address += current_symbol.symbol.size + 1
|
194
|
+
when BIND_OPCODE_ADD_ADDR_ULEB
|
195
|
+
offset, new_current_address = read_uleb(@binary_data, current_address)
|
196
|
+
current_symbol.offset = (current_symbol.offset + offset) & UINT64_MAX_VALUE
|
197
|
+
current_address = new_current_address
|
198
|
+
when BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB
|
199
|
+
offset, new_current_address = read_uleb(@binary_data, current_address)
|
200
|
+
current_symbol.offset = (current_symbol.offset + offset + UINT64_SIZE) & UINT64_MAX_VALUE
|
201
|
+
current_address = new_current_address
|
202
|
+
return [current_symbol], current_address, current_symbol
|
203
|
+
when BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
|
204
|
+
offset, current_address = read_uleb(@binary_data, current_address)
|
205
|
+
current_symbol.segment_offset = immediate
|
206
|
+
current_symbol.offset = offset
|
207
|
+
when BIND_OPCODE_SET_ADDEND_SLEB
|
208
|
+
_, current_address = read_uleb(@binary_data, current_address)
|
209
|
+
when BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED
|
210
|
+
result = current_symbol.dup
|
211
|
+
current_symbol.offset = (
|
212
|
+
current_symbol.offset + (immediate * UINT64_SIZE) + UINT64_SIZE
|
213
|
+
) & UINT64_MAX_VALUE
|
214
|
+
return [result], current_address, current_symbol
|
215
|
+
when BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB
|
216
|
+
count, current_address = read_uleb(@binary_data, current_address)
|
217
|
+
skipping, current_address = read_uleb(@binary_data, current_address)
|
218
|
+
|
219
|
+
results = []
|
220
|
+
count.times do
|
221
|
+
results << current_symbol.dup
|
222
|
+
current_symbol.offset = (current_symbol.offset + skipping + UINT64_SIZE) & UINT64_MAX_VALUE
|
223
|
+
end
|
224
|
+
|
225
|
+
return results, current_address, current_symbol
|
226
|
+
when BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB
|
227
|
+
count, current_address = read_uleb(@binary_data, current_address)
|
228
|
+
current_symbol.library = count
|
229
|
+
when BIND_OPCODE_DO_BIND
|
230
|
+
result = current_symbol.dup
|
231
|
+
current_symbol.offset = (current_symbol.offset + UINT64_SIZE) & UINT64_MAX_VALUE
|
232
|
+
return [result], current_address, current_symbol
|
233
|
+
end
|
234
|
+
end
|
235
|
+
[[], current_address, current_symbol]
|
236
|
+
end
|
237
|
+
|
238
|
+
def read_byte(binary_data, address)
|
239
|
+
binary_data[address, 1].unpack1('C')
|
240
|
+
end
|
241
|
+
|
242
|
+
def read_little_endian_signed_integer(binary_data, address)
|
243
|
+
binary_data[address, 4].unpack1('l<')
|
244
|
+
end
|
245
|
+
|
246
|
+
def read_uleb(binary_data, address)
|
247
|
+
next_byte = 0
|
248
|
+
size = 0
|
249
|
+
result = 0
|
250
|
+
|
251
|
+
loop do
|
252
|
+
next_byte = read_byte(binary_data, address)
|
253
|
+
address += 1
|
254
|
+
bytes = next_byte & 0x7F
|
255
|
+
shifted = bytes << (size * 7)
|
256
|
+
|
257
|
+
size += 1
|
258
|
+
result |= shifted
|
259
|
+
break if next_byte.nobits?(0x80)
|
260
|
+
end
|
261
|
+
|
262
|
+
[result, address]
|
263
|
+
end
|
264
|
+
|
265
|
+
def read_null_terminated_string(data)
|
266
|
+
data.unpack1('Z*')
|
267
|
+
end
|
268
|
+
|
269
|
+
def vm_address(file_offset, macho)
|
270
|
+
load_commands = macho.load_commands.select { |lc| lc.type == :LC_SEGMENT_64 || lc.type == :LC_SEGMENT } # rubocop:disable Naming/VariableNumber
|
271
|
+
load_commands.each do |lc|
|
272
|
+
next unless file_offset >= lc.fileoff && file_offset < (lc.fileoff + lc.filesize)
|
273
|
+
unless lc.respond_to?(:sections)
|
274
|
+
Logger.error 'Load command does not support sections function'
|
275
|
+
next
|
276
|
+
end
|
277
|
+
|
278
|
+
lc.sections.each do |section|
|
279
|
+
if file_offset >= section.offset && file_offset < (section.offset) + section.size
|
280
|
+
return section.addr + (file_offset - section.offset)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
nil
|
285
|
+
end
|
286
|
+
|
287
|
+
def parse_list(bytes, start, size)
|
288
|
+
data_pointer = bytes[start..]
|
289
|
+
file_offset = start
|
290
|
+
pointer_size = 4
|
291
|
+
class_pointers = []
|
292
|
+
|
293
|
+
(size / pointer_size).to_i.times do
|
294
|
+
pointer = data_pointer.unpack1('l<')
|
295
|
+
class_pointers << [pointer, file_offset]
|
296
|
+
data_pointer = data_pointer[pointer_size..]
|
297
|
+
file_offset += pointer_size
|
298
|
+
end
|
299
|
+
|
300
|
+
class_pointers
|
301
|
+
end
|
302
|
+
|
303
|
+
def get_indirect_relative_offset(type_file_address, protocol_descriptor)
|
304
|
+
vm_start = vm_address(type_file_address, @macho_file)
|
305
|
+
return nil if vm_start.nil?
|
306
|
+
if (vm_start + protocol_descriptor).odd?
|
307
|
+
(vm_start + protocol_descriptor) & ~1
|
308
|
+
elsif vm_start + protocol_descriptor > 0
|
309
|
+
vm_start + protocol_descriptor
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
class BoundSymbol
|
315
|
+
attr_accessor :segment_offset, :library, :offset, :symbol, :address
|
316
|
+
|
317
|
+
def initialize(segment_offset:, library:, offset:, symbol:)
|
318
|
+
@segment_offset = segment_offset
|
319
|
+
@library = library
|
320
|
+
@offset = offset
|
321
|
+
@symbol = symbol
|
322
|
+
@address = 0
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
data/lib/utils/network.rb
CHANGED
@@ -11,22 +11,22 @@ module EmergeCLI
|
|
11
11
|
RETRY_DELAY = 5
|
12
12
|
MAX_RETRIES = 3
|
13
13
|
|
14
|
-
def initialize(api_token
|
14
|
+
def initialize(api_token: nil, base_url: EMERGE_API_PROD_URL)
|
15
15
|
@base_url = base_url
|
16
16
|
@api_token = api_token
|
17
17
|
@internet = Async::HTTP::Internet.new
|
18
18
|
end
|
19
19
|
|
20
|
-
def get(path:, headers: {})
|
21
|
-
request(:get, path, nil, headers)
|
20
|
+
def get(path:, headers: {}, max_retries: MAX_RETRIES)
|
21
|
+
request(:get, path, nil, headers, nil, max_retries)
|
22
22
|
end
|
23
23
|
|
24
|
-
def post(path:, body:, headers: {})
|
25
|
-
request(:post, path, body, headers)
|
24
|
+
def post(path:, body:, headers: {}, query: nil, max_retries: MAX_RETRIES)
|
25
|
+
request(:post, path, body, headers, query, max_retries)
|
26
26
|
end
|
27
27
|
|
28
|
-
def put(path:, body:, headers: {})
|
29
|
-
request(:put, path, body, headers)
|
28
|
+
def put(path:, body:, headers: {}, max_retries: MAX_RETRIES)
|
29
|
+
request(:put, path, body, headers, nil, max_retries)
|
30
30
|
end
|
31
31
|
|
32
32
|
def close
|
@@ -35,18 +35,23 @@ module EmergeCLI
|
|
35
35
|
|
36
36
|
private
|
37
37
|
|
38
|
-
def request(method, path, body, custom_headers)
|
38
|
+
def request(method, path, body, custom_headers, query = nil, max_retries = MAX_RETRIES)
|
39
39
|
uri = if path.start_with?('http')
|
40
40
|
URI.parse(path)
|
41
41
|
else
|
42
|
-
URI
|
42
|
+
query_string = query ? URI.encode_www_form(query) : nil
|
43
|
+
URI::HTTPS.build(
|
44
|
+
host: @base_url,
|
45
|
+
path: path,
|
46
|
+
query: query_string
|
47
|
+
)
|
43
48
|
end
|
44
49
|
absolute_uri = uri.to_s
|
45
50
|
|
46
51
|
headers = {
|
47
|
-
'X-API-Token' => @api_token,
|
48
52
|
'User-Agent' => "emerge-cli/#{EmergeCLI::VERSION}"
|
49
53
|
}
|
54
|
+
headers['X-API-Token'] = @api_token if @api_token
|
50
55
|
headers['Content-Type'] = 'application/json' if method == :post && body.is_a?(Hash)
|
51
56
|
headers.merge!(custom_headers)
|
52
57
|
|
@@ -66,10 +71,10 @@ module EmergeCLI
|
|
66
71
|
response
|
67
72
|
rescue StandardError => e
|
68
73
|
retries += 1
|
69
|
-
if retries <=
|
74
|
+
if retries <= max_retries
|
70
75
|
delay = RETRY_DELAY * retries
|
71
76
|
error_message = e.message
|
72
|
-
Logger.warn "Request failed (attempt #{retries}/#{
|
77
|
+
Logger.warn "Request failed (attempt #{retries}/#{max_retries}): #{error_message}"
|
73
78
|
Logger.warn "Retrying in #{delay} seconds..."
|
74
79
|
|
75
80
|
begin
|
@@ -82,7 +87,9 @@ module EmergeCLI
|
|
82
87
|
sleep delay
|
83
88
|
retry
|
84
89
|
else
|
85
|
-
|
90
|
+
unless max_retries == 0
|
91
|
+
Logger.error "Request failed after #{max_retries} attempts: #{absolute_uri} #{e.message}"
|
92
|
+
end
|
86
93
|
raise e
|
87
94
|
end
|
88
95
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module EmergeCLI
|
4
|
+
module Utils
|
5
|
+
class VersionCheck
|
6
|
+
def initialize(network: EmergeCLI::Network.new)
|
7
|
+
@network = network
|
8
|
+
end
|
9
|
+
|
10
|
+
def check_version
|
11
|
+
Sync do
|
12
|
+
response = @network.get(
|
13
|
+
path: 'https://rubygems.org/api/v1/gems/emerge.json',
|
14
|
+
headers: {}
|
15
|
+
)
|
16
|
+
latest_version = JSON.parse(response.read).fetch('version')
|
17
|
+
current_version = EmergeCLI::VERSION
|
18
|
+
|
19
|
+
if Gem::Version.new(latest_version) > Gem::Version.new(current_version)
|
20
|
+
Logger.warn "A new version of emerge-cli is available (#{latest_version})"
|
21
|
+
Logger.warn "You are currently using version #{current_version}"
|
22
|
+
Logger.warn "To update, run: gem update emerge\n"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
rescue KeyError
|
26
|
+
Logger.error 'Failed to parse version from RubyGems API response'
|
27
|
+
rescue StandardError => e
|
28
|
+
Logger.error "Failed to check for updates: #{e.message}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/version.rb
CHANGED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
metadata
CHANGED
@@ -1,43 +1,49 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: emerge
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.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: 2024-12-
|
11
|
+
date: 2024-12-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name: async
|
14
|
+
name: async-http
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 0.86.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 0.86.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: CFPropertyList
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: '2.3'
|
34
|
+
- - ">="
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: 2.3.2
|
34
37
|
type: :runtime
|
35
38
|
prerelease: false
|
36
39
|
version_requirements: !ruby/object:Gem::Requirement
|
37
40
|
requirements:
|
38
41
|
- - "~>"
|
39
42
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
43
|
+
version: '2.3'
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 2.3.2
|
41
47
|
- !ruby/object:Gem::Dependency
|
42
48
|
name: chunky_png
|
43
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -81,19 +87,19 @@ dependencies:
|
|
81
87
|
- !ruby/object:Gem::Version
|
82
88
|
version: 0.2.1
|
83
89
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
90
|
+
name: ruby-macho
|
85
91
|
requirement: !ruby/object:Gem::Requirement
|
86
92
|
requirements:
|
87
93
|
- - "~>"
|
88
94
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
95
|
+
version: 4.1.0
|
90
96
|
type: :runtime
|
91
97
|
prerelease: false
|
92
98
|
version_requirements: !ruby/object:Gem::Requirement
|
93
99
|
requirements:
|
94
100
|
- - "~>"
|
95
101
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
102
|
+
version: 4.1.0
|
97
103
|
- !ruby/object:Gem::Dependency
|
98
104
|
name: ruby_tree_sitter
|
99
105
|
requirement: !ruby/object:Gem::Requirement
|
@@ -165,6 +171,10 @@ files:
|
|
165
171
|
- lib/commands/config/snapshots/snapshots_ios.rb
|
166
172
|
- lib/commands/global_options.rb
|
167
173
|
- lib/commands/integrate/fastlane.rb
|
174
|
+
- lib/commands/order_files/download_order_files.rb
|
175
|
+
- lib/commands/order_files/validate_linkmaps.rb
|
176
|
+
- lib/commands/reaper/reaper.rb
|
177
|
+
- lib/commands/snapshots/validate_app.rb
|
168
178
|
- lib/commands/upload/snapshots/client_libraries/default.rb
|
169
179
|
- lib/commands/upload/snapshots/client_libraries/paparazzi.rb
|
170
180
|
- lib/commands/upload/snapshots/client_libraries/roborazzi.rb
|
@@ -172,15 +182,24 @@ files:
|
|
172
182
|
- lib/commands/upload/snapshots/snapshots.rb
|
173
183
|
- lib/emerge_cli.rb
|
174
184
|
- lib/reaper/ast_parser.rb
|
185
|
+
- lib/reaper/code_deleter.rb
|
175
186
|
- lib/utils/git.rb
|
176
187
|
- lib/utils/git_info_provider.rb
|
177
188
|
- lib/utils/git_result.rb
|
178
189
|
- lib/utils/github.rb
|
179
190
|
- lib/utils/logger.rb
|
191
|
+
- lib/utils/macho_parser.rb
|
180
192
|
- lib/utils/network.rb
|
181
193
|
- lib/utils/profiler.rb
|
182
194
|
- lib/utils/project_detector.rb
|
195
|
+
- lib/utils/version_check.rb
|
183
196
|
- lib/version.rb
|
197
|
+
- parsers/libtree-sitter-java-darwin-arm64.dylib
|
198
|
+
- parsers/libtree-sitter-java-linux-x86_64.so
|
199
|
+
- parsers/libtree-sitter-kotlin-darwin-arm64.dylib
|
200
|
+
- parsers/libtree-sitter-kotlin-linux-x86_64.so
|
201
|
+
- parsers/libtree-sitter-swift-darwin-arm64.dylib
|
202
|
+
- parsers/libtree-sitter-swift-linux-x86_64.so
|
184
203
|
homepage: https://github.com/EmergeTools/emerge-cli
|
185
204
|
licenses:
|
186
205
|
- MIT
|
@@ -204,7 +223,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
204
223
|
- !ruby/object:Gem::Version
|
205
224
|
version: '0'
|
206
225
|
requirements: []
|
207
|
-
rubygems_version: 3.
|
226
|
+
rubygems_version: 3.5.11
|
208
227
|
signing_key:
|
209
228
|
specification_version: 4
|
210
229
|
summary: Emerge CLI
|