kintsugi 0.4.3 → 0.5.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/release.yml +33 -0
- data/.github/workflows/{ci.yml → tests.yml} +2 -1
- data/.rubocop.yml +3 -0
- data/bin/kintsugi +3 -29
- data/kintsugi.gemspec +1 -0
- data/lib/kintsugi/apply_change_to_project.rb +264 -132
- data/lib/kintsugi/cli.rb +2 -1
- data/lib/kintsugi/merge.rb +146 -0
- data/lib/kintsugi/version.rb +1 -1
- data/lib/kintsugi/xcodeproj_extensions.rb +122 -0
- data/lib/kintsugi.rb +29 -148
- data/spec/kintsugi_apply_change_to_project_spec.rb +549 -52
- data/spec/kintsugi_integration_spec.rb +148 -0
- data/spec/spec_helper.rb +3 -0
- metadata +22 -5
@@ -5,6 +5,7 @@
|
|
5
5
|
require "xcodeproj"
|
6
6
|
|
7
7
|
require_relative "utils"
|
8
|
+
require_relative "error"
|
8
9
|
require_relative "xcodeproj_extensions"
|
9
10
|
|
10
11
|
module Kintsugi
|
@@ -28,26 +29,28 @@ module Kintsugi
|
|
28
29
|
puts "Warning: Main group doesn't exist, ignoring changes to it."
|
29
30
|
else
|
30
31
|
apply_change_to_component(project.root_object, "mainGroup",
|
31
|
-
change["rootObject"]["mainGroup"])
|
32
|
+
change["rootObject"]["mainGroup"], "rootObject")
|
32
33
|
end
|
33
34
|
end
|
34
35
|
|
35
36
|
unless change["rootObject"]["projectReferences"].nil?
|
36
37
|
apply_change_to_component(project.root_object, "projectReferences",
|
37
|
-
change["rootObject"]["projectReferences"])
|
38
|
+
change["rootObject"]["projectReferences"], "rootObject")
|
38
39
|
end
|
39
40
|
|
40
41
|
apply_change_to_component(project, "rootObject",
|
41
42
|
change["rootObject"].reject { |key|
|
42
43
|
%w[mainGroup projectReferences].include?(key)
|
43
|
-
})
|
44
|
+
}, "")
|
44
45
|
end
|
45
46
|
|
46
47
|
private
|
47
48
|
|
48
|
-
def apply_change_to_component(parent_component, change_name, change)
|
49
|
+
def apply_change_to_component(parent_component, change_name, change, parent_change_path)
|
49
50
|
return if change_name == "displayName"
|
50
51
|
|
52
|
+
change_path = parent_change_path.empty? ? change_name : "#{parent_change_path}/#{change_name}"
|
53
|
+
|
51
54
|
attribute_name = attribute_name_from_change_name(change_name)
|
52
55
|
if simple_attribute?(parent_component, attribute_name)
|
53
56
|
apply_change_to_simple_attribute(parent_component, attribute_name, change)
|
@@ -61,7 +64,7 @@ module Kintsugi
|
|
61
64
|
component = child_component(parent_component, change_name)
|
62
65
|
|
63
66
|
if component.nil?
|
64
|
-
add_missing_component_if_valid(parent_component, change_name, change)
|
67
|
+
add_missing_component_if_valid(parent_component, change_name, change, change_path)
|
65
68
|
return
|
66
69
|
end
|
67
70
|
end
|
@@ -75,11 +78,12 @@ module Kintsugi
|
|
75
78
|
|
76
79
|
(change[:added] || []).each do |added_change|
|
77
80
|
is_object_list = component.is_a?(Xcodeproj::Project::ObjectList)
|
78
|
-
add_child_to_component(is_object_list ? parent_component : component, added_change
|
81
|
+
add_child_to_component(is_object_list ? parent_component : component, added_change,
|
82
|
+
change_path)
|
79
83
|
end
|
80
84
|
|
81
85
|
subchanges_of_change(change).each do |subchange_name, subchange|
|
82
|
-
apply_change_to_component(component, subchange_name, subchange)
|
86
|
+
apply_change_to_component(component, subchange_name, subchange, change_path)
|
83
87
|
end
|
84
88
|
end
|
85
89
|
|
@@ -92,16 +96,16 @@ module Kintsugi
|
|
92
96
|
end
|
93
97
|
|
94
98
|
def attribute_name_from_change_name(change_name)
|
95
|
-
if change_name
|
99
|
+
if %w[fileEncoding repositoryURL].include?(change_name)
|
96
100
|
change_name.to_sym
|
97
101
|
else
|
98
102
|
Xcodeproj::Project::Object::CaseConverter.convert_to_ruby(change_name)
|
99
103
|
end
|
100
104
|
end
|
101
105
|
|
102
|
-
def add_missing_component_if_valid(parent_component, change_name, change)
|
106
|
+
def add_missing_component_if_valid(parent_component, change_name, change, change_path)
|
103
107
|
if change[:added] && change.compact.count == 1
|
104
|
-
add_child_to_component(parent_component, change[:added])
|
108
|
+
add_child_to_component(parent_component, change[:added], change_path)
|
105
109
|
return
|
106
110
|
end
|
107
111
|
|
@@ -111,10 +115,7 @@ module Kintsugi
|
|
111
115
|
|
112
116
|
def replace_component_with_new_type(parent_component, name_in_parent_component, change)
|
113
117
|
old_component = parent_component.send(name_in_parent_component)
|
114
|
-
|
115
|
-
new_component = parent_component.project.new(
|
116
|
-
Module.const_get("Xcodeproj::Project::#{change["isa"][:added]}")
|
117
|
-
)
|
118
|
+
new_component = component_of_new_type(parent_component, change, old_component)
|
118
119
|
|
119
120
|
copy_attributes_to_new_component(old_component, new_component)
|
120
121
|
|
@@ -122,6 +123,27 @@ module Kintsugi
|
|
122
123
|
new_component
|
123
124
|
end
|
124
125
|
|
126
|
+
def component_of_new_type(parent_component, change, old_component)
|
127
|
+
if change["isa"][:added] == "PBXFileReference"
|
128
|
+
path = (change["path"] && change["path"][:added]) || old_component.path
|
129
|
+
case parent_component
|
130
|
+
when Xcodeproj::Project::XCBuildConfiguration
|
131
|
+
parent_component.base_configuration_reference = find_file(parent_component.project, path)
|
132
|
+
return parent_component.base_configuration_reference
|
133
|
+
when Xcodeproj::Project::PBXNativeTarget
|
134
|
+
parent_component.product_reference = find_file(parent_component.project, path)
|
135
|
+
return parent_component.product_reference
|
136
|
+
when Xcodeproj::Project::PBXBuildFile
|
137
|
+
parent_component.file_ref = find_file(parent_component.project, path)
|
138
|
+
return parent_component.file_ref
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
parent_component.project.new(
|
143
|
+
Module.const_get("Xcodeproj::Project::#{change["isa"][:added]}")
|
144
|
+
)
|
145
|
+
end
|
146
|
+
|
125
147
|
def copy_attributes_to_new_component(old_component, new_component)
|
126
148
|
# The change won't describe the attributes that haven't changed, therefore the attributes
|
127
149
|
# are copied to the new component.
|
@@ -164,15 +186,8 @@ module Kintsugi
|
|
164
186
|
end
|
165
187
|
|
166
188
|
def simple_attribute_value_with_change(old_value, change)
|
167
|
-
|
168
|
-
|
169
|
-
if change.key?(:removed)
|
170
|
-
new_value = apply_removal_to_simple_attribute(old_value, change[:removed], change[:added])
|
171
|
-
end
|
172
|
-
|
173
|
-
if change.key?(:added)
|
174
|
-
new_value = apply_addition_to_simple_attribute(old_value, change[:added])
|
175
|
-
end
|
189
|
+
type = simple_attribute_type(old_value, change[:removed], change[:added])
|
190
|
+
new_value = new_simple_attribute_value(type, old_value, change[:removed], change[:added])
|
176
191
|
|
177
192
|
subchanges_of_change(change).each do |subchange_name, subchange_value|
|
178
193
|
new_value = new_value || old_value || {}
|
@@ -183,13 +198,67 @@ module Kintsugi
|
|
183
198
|
new_value
|
184
199
|
end
|
185
200
|
|
186
|
-
def
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
201
|
+
def simple_attribute_type(old_value, removed_change, added_change)
|
202
|
+
types = [old_value.class, removed_change.class, added_change.class]
|
203
|
+
|
204
|
+
if types.include?(Hash)
|
205
|
+
unless types.to_set.subset?([Hash, NilClass].to_set)
|
206
|
+
raise MergeError, "Cannot apply changes because the types are not compatible. Existing " \
|
207
|
+
"value: '#{old_value}', removed change: '#{removed_change}', added change: " \
|
208
|
+
"'#{added_change}'"
|
209
|
+
end
|
210
|
+
Hash
|
211
|
+
elsif types.include?(Array)
|
212
|
+
unless types.to_set.subset?([Array, String, NilClass].to_set)
|
213
|
+
raise MergeError, "Cannot apply changes because the types are not compatible. Existing " \
|
214
|
+
"value: '#{old_value}', removed change: '#{removed_change}', added change: " \
|
215
|
+
"'#{added_change}'"
|
216
|
+
end
|
217
|
+
Array
|
218
|
+
elsif types.include?(String)
|
219
|
+
unless types.to_set.subset?([String, NilClass].to_set)
|
220
|
+
raise MergeError, "Cannot apply changes because the types are not compatible. Existing " \
|
221
|
+
"value: '#{old_value}', removed change: '#{removed_change}', added change: " \
|
222
|
+
"'#{added_change}'"
|
223
|
+
end
|
224
|
+
String
|
225
|
+
else
|
226
|
+
raise MergeError, "Unsupported types of all of the values. Existing value: " \
|
227
|
+
"'#{old_value}', removed change: '#{removed_change}', added change: '#{added_change}'"
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def new_simple_attribute_value(type, old_value, removed_change, added_change)
|
232
|
+
if type == Hash
|
233
|
+
new_hash_simple_attribute_value(old_value, removed_change, added_change)
|
234
|
+
elsif type == Array
|
235
|
+
new_array_simple_attribute_value(old_value, removed_change, added_change)
|
236
|
+
elsif type == String
|
237
|
+
new_string_simple_attribute_value(old_value, removed_change, added_change)
|
238
|
+
else
|
239
|
+
raise MergeError, "Unsupported types of all of the values. Existing value: " \
|
240
|
+
"'#{old_value}', removed change: '#{removed_change}', added change: '#{added_change}'"
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
def new_hash_simple_attribute_value(old_value, removed_change, added_change)
|
245
|
+
return added_change if ((old_value || {}).to_a - (removed_change || {}).to_a).empty?
|
246
|
+
|
247
|
+
# First apply the added change to see if there are any conflicts with it.
|
248
|
+
new_value = (old_value || {}).merge(added_change || {})
|
249
|
+
|
250
|
+
unless (old_value.to_a - new_value.to_a).empty?
|
251
|
+
raise MergeError, "New hash #{change} contains values that conflict with old hash " \
|
252
|
+
"#{old_value}"
|
253
|
+
end
|
254
|
+
|
255
|
+
if removed_change.nil?
|
256
|
+
return new_value
|
257
|
+
end
|
258
|
+
|
259
|
+
new_value
|
260
|
+
.reject do |key, value|
|
261
|
+
if value != removed_change[key] && value != (added_change || {})[key]
|
193
262
|
raise MergeError, "Trying to remove value '#{removed_change[key]}' of hash with key " \
|
194
263
|
"'#{key}' but it changed to #{value}. This is considered a conflict that should be " \
|
195
264
|
"resolved manually."
|
@@ -197,41 +266,31 @@ module Kintsugi
|
|
197
266
|
|
198
267
|
removed_change.key?(key)
|
199
268
|
end
|
200
|
-
|
201
|
-
if old_value != removed_change && !old_value.nil? && added_change != old_value
|
202
|
-
raise MergeError, "Trying to remove value '#{removed_change}', but the existing value " \
|
203
|
-
"is '#{old_value}'. This is considered a conflict that should be resolved manually."
|
204
|
-
end
|
269
|
+
end
|
205
270
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
271
|
+
def new_array_simple_attribute_value(old_value, removed_change, added_change)
|
272
|
+
if old_value.is_a?(String)
|
273
|
+
old_value = [old_value]
|
274
|
+
end
|
275
|
+
if removed_change.is_a?(String)
|
276
|
+
removed_change = [removed_change]
|
277
|
+
end
|
278
|
+
if added_change.is_a?(String)
|
279
|
+
added_change = [added_change]
|
211
280
|
end
|
212
|
-
end
|
213
281
|
|
214
|
-
|
215
|
-
case change
|
216
|
-
when Array
|
217
|
-
(old_value || []) + change
|
218
|
-
when Hash
|
219
|
-
old_value ||= {}
|
220
|
-
new_value = old_value.merge(change)
|
282
|
+
return added_change if ((old_value || []) - (removed_change || [])).empty?
|
221
283
|
|
222
|
-
|
223
|
-
|
224
|
-
"#{old_value}"
|
225
|
-
end
|
284
|
+
(old_value || []) + (added_change || []) - (removed_change || [])
|
285
|
+
end
|
226
286
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
nil
|
232
|
-
else
|
233
|
-
raise MergeError, "Unsupported change #{change} of type #{change.class}"
|
287
|
+
def new_string_simple_attribute_value(old_value, removed_change, added_change)
|
288
|
+
if old_value != removed_change && !old_value.nil? && added_change != old_value
|
289
|
+
raise MergeError, "Trying to remove value '#{removed_change || "nil"}', but the existing " \
|
290
|
+
"value is '#{old_value}'. This is considered a conflict that should be resolved manually."
|
234
291
|
end
|
292
|
+
|
293
|
+
added_change
|
235
294
|
end
|
236
295
|
|
237
296
|
def remove_component(component, change)
|
@@ -260,56 +319,94 @@ module Kintsugi
|
|
260
319
|
end
|
261
320
|
end
|
262
321
|
|
263
|
-
def add_child_to_component(component, change)
|
322
|
+
def add_child_to_component(component, change, component_change_path)
|
323
|
+
change_path = "#{component_change_path}/#{change["displayName"]}"
|
324
|
+
|
264
325
|
if change["ProjectRef"] && change["ProductGroup"]
|
265
|
-
add_subproject_reference(component, change)
|
326
|
+
add_subproject_reference(component, change, change_path)
|
266
327
|
return
|
267
328
|
end
|
268
329
|
|
269
330
|
case change["isa"]
|
270
331
|
when "PBXNativeTarget"
|
271
|
-
add_target(component, change)
|
332
|
+
add_target(component, change, change_path)
|
272
333
|
when "PBXAggregateTarget"
|
273
|
-
add_aggregate_target(component, change)
|
334
|
+
add_aggregate_target(component, change, change_path)
|
274
335
|
when "PBXFileReference"
|
275
|
-
add_file_reference(component, change)
|
336
|
+
add_file_reference(component, change, change_path)
|
276
337
|
when "PBXGroup"
|
277
|
-
add_group(component, change)
|
338
|
+
add_group(component, change, change_path)
|
278
339
|
when "PBXContainerItemProxy"
|
279
|
-
add_container_item_proxy(component, change)
|
340
|
+
add_container_item_proxy(component, change, change_path)
|
280
341
|
when "PBXTargetDependency"
|
281
|
-
add_target_dependency(component, change)
|
342
|
+
add_target_dependency(component, change, change_path)
|
282
343
|
when "PBXBuildFile"
|
283
|
-
add_build_file(component, change)
|
344
|
+
add_build_file(component, change, change_path)
|
284
345
|
when "XCConfigurationList"
|
285
|
-
add_build_configuration_list(component, change)
|
346
|
+
add_build_configuration_list(component, change, change_path)
|
286
347
|
when "XCBuildConfiguration"
|
287
|
-
add_build_configuration(component, change)
|
348
|
+
add_build_configuration(component, change, change_path)
|
288
349
|
when "PBXHeadersBuildPhase"
|
289
|
-
add_headers_build_phase(component, change)
|
350
|
+
add_headers_build_phase(component, change, change_path)
|
290
351
|
when "PBXSourcesBuildPhase"
|
291
|
-
add_sources_build_phase(component, change)
|
352
|
+
add_sources_build_phase(component, change, change_path)
|
292
353
|
when "PBXCopyFilesBuildPhase"
|
293
|
-
add_copy_files_build_phase(component, change)
|
354
|
+
add_copy_files_build_phase(component, change, change_path)
|
294
355
|
when "PBXShellScriptBuildPhase"
|
295
|
-
add_shell_script_build_phase(component, change)
|
356
|
+
add_shell_script_build_phase(component, change, change_path)
|
296
357
|
when "PBXFrameworksBuildPhase"
|
297
|
-
add_frameworks_build_phase(component, change)
|
358
|
+
add_frameworks_build_phase(component, change, change_path)
|
298
359
|
when "PBXResourcesBuildPhase"
|
299
|
-
add_resources_build_phase(component, change)
|
360
|
+
add_resources_build_phase(component, change, change_path)
|
300
361
|
when "PBXBuildRule"
|
301
|
-
add_build_rule(component, change)
|
362
|
+
add_build_rule(component, change, change_path)
|
302
363
|
when "PBXVariantGroup"
|
303
|
-
add_variant_group(component, change)
|
364
|
+
add_variant_group(component, change, change_path)
|
304
365
|
when "PBXReferenceProxy"
|
305
|
-
add_reference_proxy(component, change)
|
366
|
+
add_reference_proxy(component, change, change_path)
|
367
|
+
when "XCSwiftPackageProductDependency"
|
368
|
+
add_swift_package_product_dependency(component, change, change_path)
|
369
|
+
when "XCRemoteSwiftPackageReference"
|
370
|
+
add_remote_swift_package_reference(component, change, change_path)
|
306
371
|
else
|
307
372
|
raise MergeError, "Trying to add unsupported component type #{change["isa"]}. Full " \
|
308
373
|
"component change is: #{change}"
|
309
374
|
end
|
310
375
|
end
|
311
376
|
|
312
|
-
def
|
377
|
+
def add_remote_swift_package_reference(containing_component, change, change_path)
|
378
|
+
remote_swift_package_reference =
|
379
|
+
containing_component.project.new(Xcodeproj::Project::XCRemoteSwiftPackageReference)
|
380
|
+
add_attributes_to_component(remote_swift_package_reference, change, change_path)
|
381
|
+
|
382
|
+
case containing_component
|
383
|
+
when Xcodeproj::Project::XCSwiftPackageProductDependency
|
384
|
+
containing_component.package = remote_swift_package_reference
|
385
|
+
when Xcodeproj::Project::PBXProject
|
386
|
+
containing_component.package_references << remote_swift_package_reference
|
387
|
+
else
|
388
|
+
raise MergeError, "Trying to add remote swift package reference to an unsupported " \
|
389
|
+
"component type #{containing_component.isa}. Change is: #{change}"
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
def add_swift_package_product_dependency(containing_component, change, change_path)
|
394
|
+
swift_package_product_dependency =
|
395
|
+
containing_component.project.new(Xcodeproj::Project::XCSwiftPackageProductDependency)
|
396
|
+
add_attributes_to_component(swift_package_product_dependency, change, change_path)
|
397
|
+
|
398
|
+
case containing_component
|
399
|
+
when Xcodeproj::Project::PBXBuildFile
|
400
|
+
containing_component.product_ref = swift_package_product_dependency
|
401
|
+
when Xcodeproj::Project::PBXNativeTarget
|
402
|
+
containing_component.package_product_dependencies << swift_package_product_dependency
|
403
|
+
else
|
404
|
+
raise MergeError, "Trying to add swift package product dependency to an unsupported " \
|
405
|
+
"component type #{containing_component.isa}. Change is: #{change}"
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
def add_reference_proxy(containing_component, change, change_path)
|
313
410
|
case containing_component
|
314
411
|
when Xcodeproj::Project::PBXBuildFile
|
315
412
|
# If there are two file references that refer to the same file, one with a build file and
|
@@ -331,74 +428,78 @@ module Kintsugi
|
|
331
428
|
when Xcodeproj::Project::PBXGroup
|
332
429
|
reference_proxy = containing_component.project.new(Xcodeproj::Project::PBXReferenceProxy)
|
333
430
|
containing_component << reference_proxy
|
334
|
-
add_attributes_to_component(reference_proxy, change)
|
431
|
+
add_attributes_to_component(reference_proxy, change, change_path)
|
335
432
|
else
|
336
433
|
raise MergeError, "Trying to add reference proxy to an unsupported component type " \
|
337
434
|
"#{containing_component.isa}. Change is: #{change}"
|
338
435
|
end
|
339
436
|
end
|
340
437
|
|
341
|
-
def add_variant_group(containing_component, change)
|
438
|
+
def add_variant_group(containing_component, change, change_path)
|
342
439
|
case containing_component
|
343
440
|
when Xcodeproj::Project::PBXBuildFile
|
344
441
|
containing_component.file_ref =
|
345
442
|
find_variant_group(containing_component.project, change["displayName"])
|
346
443
|
when Xcodeproj::Project::PBXGroup, Xcodeproj::Project::PBXVariantGroup
|
444
|
+
unless adding_files_and_groups_allowed?(change_path)
|
445
|
+
return
|
446
|
+
end
|
447
|
+
|
347
448
|
variant_group = containing_component.project.new(Xcodeproj::Project::PBXVariantGroup)
|
348
449
|
containing_component.children << variant_group
|
349
|
-
add_attributes_to_component(variant_group, change)
|
450
|
+
add_attributes_to_component(variant_group, change, change_path)
|
350
451
|
else
|
351
452
|
raise MergeError, "Trying to add variant group to an unsupported component type " \
|
352
453
|
"#{containing_component.isa}. Change is: #{change}"
|
353
454
|
end
|
354
455
|
end
|
355
456
|
|
356
|
-
def add_build_rule(target, change)
|
457
|
+
def add_build_rule(target, change, change_path)
|
357
458
|
build_rule = target.project.new(Xcodeproj::Project::PBXBuildRule)
|
358
459
|
target.build_rules << build_rule
|
359
|
-
add_attributes_to_component(build_rule, change)
|
460
|
+
add_attributes_to_component(build_rule, change, change_path)
|
360
461
|
end
|
361
462
|
|
362
|
-
def add_shell_script_build_phase(target, change)
|
463
|
+
def add_shell_script_build_phase(target, change, change_path)
|
363
464
|
build_phase = target.new_shell_script_build_phase(change["displayName"])
|
364
|
-
add_attributes_to_component(build_phase, change)
|
465
|
+
add_attributes_to_component(build_phase, change, change_path)
|
365
466
|
end
|
366
467
|
|
367
|
-
def add_headers_build_phase(target, change)
|
368
|
-
add_attributes_to_component(target.headers_build_phase, change)
|
468
|
+
def add_headers_build_phase(target, change, change_path)
|
469
|
+
add_attributes_to_component(target.headers_build_phase, change, change_path)
|
369
470
|
end
|
370
471
|
|
371
|
-
def add_sources_build_phase(target, change)
|
372
|
-
add_attributes_to_component(target.source_build_phase, change)
|
472
|
+
def add_sources_build_phase(target, change, change_path)
|
473
|
+
add_attributes_to_component(target.source_build_phase, change, change_path)
|
373
474
|
end
|
374
475
|
|
375
|
-
def add_frameworks_build_phase(target, change)
|
376
|
-
add_attributes_to_component(target.frameworks_build_phase, change)
|
476
|
+
def add_frameworks_build_phase(target, change, change_path)
|
477
|
+
add_attributes_to_component(target.frameworks_build_phase, change, change_path)
|
377
478
|
end
|
378
479
|
|
379
|
-
def add_resources_build_phase(target, change)
|
380
|
-
add_attributes_to_component(target.resources_build_phase, change)
|
480
|
+
def add_resources_build_phase(target, change, change_path)
|
481
|
+
add_attributes_to_component(target.resources_build_phase, change, change_path)
|
381
482
|
end
|
382
483
|
|
383
|
-
def add_copy_files_build_phase(target, change)
|
484
|
+
def add_copy_files_build_phase(target, change, change_path)
|
384
485
|
copy_files_phase_name = change["displayName"] == "CopyFiles" ? nil : change["displayName"]
|
385
486
|
copy_files_phase = target.new_copy_files_build_phase(copy_files_phase_name)
|
386
487
|
|
387
|
-
add_attributes_to_component(copy_files_phase, change)
|
488
|
+
add_attributes_to_component(copy_files_phase, change, change_path)
|
388
489
|
end
|
389
490
|
|
390
|
-
def add_build_configuration_list(target, change)
|
491
|
+
def add_build_configuration_list(target, change, change_path)
|
391
492
|
target.build_configuration_list = target.project.new(Xcodeproj::Project::XCConfigurationList)
|
392
|
-
add_attributes_to_component(target.build_configuration_list, change)
|
493
|
+
add_attributes_to_component(target.build_configuration_list, change, change_path)
|
393
494
|
end
|
394
495
|
|
395
|
-
def add_build_configuration(configuration_list, change)
|
496
|
+
def add_build_configuration(configuration_list, change, change_path)
|
396
497
|
build_configuration = configuration_list.project.new(Xcodeproj::Project::XCBuildConfiguration)
|
397
498
|
configuration_list.build_configurations << build_configuration
|
398
|
-
add_attributes_to_component(build_configuration, change)
|
499
|
+
add_attributes_to_component(build_configuration, change, change_path)
|
399
500
|
end
|
400
501
|
|
401
|
-
def add_build_file(build_phase, change)
|
502
|
+
def add_build_file(build_phase, change, change_path)
|
402
503
|
if change["fileRef"].nil?
|
403
504
|
puts "Warning: Trying to add a build file without any file reference to build phase " \
|
404
505
|
"'#{build_phase}'"
|
@@ -407,7 +508,7 @@ module Kintsugi
|
|
407
508
|
|
408
509
|
build_file = build_phase.project.new(Xcodeproj::Project::PBXBuildFile)
|
409
510
|
build_phase.files << build_file
|
410
|
-
add_attributes_to_component(build_file, change)
|
511
|
+
add_attributes_to_component(build_file, change, change_path)
|
411
512
|
end
|
412
513
|
|
413
514
|
def find_variant_group(project, display_name)
|
@@ -416,7 +517,7 @@ module Kintsugi
|
|
416
517
|
end
|
417
518
|
end
|
418
519
|
|
419
|
-
def add_target_dependency(target, change)
|
520
|
+
def add_target_dependency(target, change, change_path)
|
420
521
|
target_dependency = find_target(target.project, change["displayName"])
|
421
522
|
|
422
523
|
if target_dependency
|
@@ -427,14 +528,14 @@ module Kintsugi
|
|
427
528
|
target_dependency = target.project.new(Xcodeproj::Project::PBXTargetDependency)
|
428
529
|
|
429
530
|
target.dependencies << target_dependency
|
430
|
-
add_attributes_to_component(target_dependency, change)
|
531
|
+
add_attributes_to_component(target_dependency, change, change_path)
|
431
532
|
end
|
432
533
|
|
433
534
|
def find_target(project, display_name)
|
434
535
|
project.targets.find { |target| target.display_name == display_name }
|
435
536
|
end
|
436
537
|
|
437
|
-
def add_container_item_proxy(component, change)
|
538
|
+
def add_container_item_proxy(component, change, change_path)
|
438
539
|
container_proxy = component.project.new(Xcodeproj::Project::PBXContainerItemProxy)
|
439
540
|
container_proxy.container_portal = find_containing_project_uuid(component.project, change)
|
440
541
|
|
@@ -447,7 +548,8 @@ module Kintsugi
|
|
447
548
|
raise MergeError, "Trying to add container item proxy to an unsupported component type " \
|
448
549
|
"#{containing_component.isa}. Change is: #{change}"
|
449
550
|
end
|
450
|
-
add_attributes_to_component(container_proxy, change,
|
551
|
+
add_attributes_to_component(container_proxy, change, change_path,
|
552
|
+
ignore_keys: ["containerPortal"])
|
451
553
|
end
|
452
554
|
|
453
555
|
def find_containing_project_uuid(project, container_item_proxy_change)
|
@@ -478,16 +580,22 @@ module Kintsugi
|
|
478
580
|
container_item_proxies.first.container_portal
|
479
581
|
end
|
480
582
|
|
481
|
-
def add_subproject_reference(root_object, project_reference_change)
|
583
|
+
def add_subproject_reference(root_object, project_reference_change, change_path)
|
482
584
|
filter_subproject_without_project_references = lambda do |file_reference|
|
483
585
|
root_object.project_references.find do |project_reference|
|
484
586
|
project_reference.project_ref.uuid == file_reference.uuid
|
485
587
|
end.nil?
|
486
588
|
end
|
487
589
|
subproject_reference =
|
488
|
-
find_file(root_object.project, project_reference_change["ProjectRef"],
|
590
|
+
find_file(root_object.project, project_reference_change["ProjectRef"]["path"],
|
489
591
|
file_filter: filter_subproject_without_project_references)
|
490
592
|
|
593
|
+
unless subproject_reference
|
594
|
+
raise MergeError, "No file reference was found for project reference with change " \
|
595
|
+
"#{project_reference_change}. This might mean that the file used to exist in the " \
|
596
|
+
"project the but was removed at some point"
|
597
|
+
end
|
598
|
+
|
491
599
|
attribute =
|
492
600
|
Xcodeproj::Project::PBXProject.references_by_keys_attributes
|
493
601
|
.find { |attrb| attrb.name == :project_references }
|
@@ -498,7 +606,7 @@ module Kintsugi
|
|
498
606
|
updated_project_reference_change =
|
499
607
|
change_with_updated_subproject_uuid(project_reference_change, subproject_reference.uuid)
|
500
608
|
add_attributes_to_component(project_reference, updated_project_reference_change,
|
501
|
-
ignore_keys: ["ProjectRef"])
|
609
|
+
change_path, ignore_keys: ["ProjectRef"])
|
502
610
|
end
|
503
611
|
|
504
612
|
def change_with_updated_subproject_uuid(change, subproject_reference_uuid)
|
@@ -510,19 +618,19 @@ module Kintsugi
|
|
510
618
|
new_change
|
511
619
|
end
|
512
620
|
|
513
|
-
def add_target(root_object, change)
|
621
|
+
def add_target(root_object, change, change_path)
|
514
622
|
target = root_object.project.new(Xcodeproj::Project::PBXNativeTarget)
|
515
623
|
root_object.project.targets << target
|
516
|
-
add_attributes_to_component(target, change)
|
624
|
+
add_attributes_to_component(target, change, change_path)
|
517
625
|
end
|
518
626
|
|
519
|
-
def add_aggregate_target(root_object, change)
|
627
|
+
def add_aggregate_target(root_object, change, change_path)
|
520
628
|
target = root_object.project.new(Xcodeproj::Project::PBXAggregateTarget)
|
521
629
|
root_object.project.targets << target
|
522
|
-
add_attributes_to_component(target, change)
|
630
|
+
add_attributes_to_component(target, change, change_path)
|
523
631
|
end
|
524
632
|
|
525
|
-
def add_file_reference(containing_component, change)
|
633
|
+
def add_file_reference(containing_component, change, change_path)
|
526
634
|
# base configuration reference and product reference always reference a file that exists
|
527
635
|
# inside a group, therefore in these cases the file is searched for.
|
528
636
|
# In the case of group and variant group, the file can't exist in another group, therefore a
|
@@ -530,25 +638,41 @@ module Kintsugi
|
|
530
638
|
case containing_component
|
531
639
|
when Xcodeproj::Project::XCBuildConfiguration
|
532
640
|
containing_component.base_configuration_reference =
|
533
|
-
find_file(containing_component.project, change)
|
641
|
+
find_file(containing_component.project, change["path"])
|
534
642
|
when Xcodeproj::Project::PBXNativeTarget
|
535
|
-
containing_component.product_reference =
|
643
|
+
containing_component.product_reference =
|
644
|
+
find_file(containing_component.project, change["path"])
|
536
645
|
when Xcodeproj::Project::PBXBuildFile
|
537
|
-
containing_component.file_ref = find_file(containing_component.project, change)
|
646
|
+
containing_component.file_ref = find_file(containing_component.project, change["path"])
|
538
647
|
when Xcodeproj::Project::PBXGroup, Xcodeproj::Project::PBXVariantGroup
|
648
|
+
unless adding_files_and_groups_allowed?(change_path)
|
649
|
+
return
|
650
|
+
end
|
651
|
+
|
539
652
|
file_reference = containing_component.project.new(Xcodeproj::Project::PBXFileReference)
|
540
653
|
containing_component.children << file_reference
|
541
654
|
|
542
|
-
# For some reason, `include_in_index` is set to `1` by
|
655
|
+
# For some reason, `include_in_index` is set to `1` and `source_tree` to `SDKROOT` by
|
656
|
+
# default.
|
543
657
|
file_reference.include_in_index = nil
|
544
|
-
|
658
|
+
file_reference.source_tree = nil
|
659
|
+
add_attributes_to_component(file_reference, change, change_path)
|
545
660
|
else
|
546
661
|
raise MergeError, "Trying to add file reference to an unsupported component type " \
|
547
662
|
"#{containing_component.isa}. Change is: #{change}"
|
548
663
|
end
|
549
664
|
end
|
550
665
|
|
551
|
-
def
|
666
|
+
def adding_files_and_groups_allowed?(change_path)
|
667
|
+
change_path.start_with?("rootObject/mainGroup") ||
|
668
|
+
change_path.start_with?("rootObject/projectReferences")
|
669
|
+
end
|
670
|
+
|
671
|
+
def add_group(containing_component, change, change_path)
|
672
|
+
unless adding_files_and_groups_allowed?(change_path)
|
673
|
+
return
|
674
|
+
end
|
675
|
+
|
552
676
|
case containing_component
|
553
677
|
when Xcodeproj::Project::ObjectDictionary
|
554
678
|
# It is assumed that an `ObjectDictionary` always represents a project reference.
|
@@ -562,25 +686,29 @@ module Kintsugi
|
|
562
686
|
"#{containing_component.isa}. Change is: #{change}"
|
563
687
|
end
|
564
688
|
|
565
|
-
add_attributes_to_component(new_group, change)
|
689
|
+
add_attributes_to_component(new_group, change, change_path)
|
566
690
|
end
|
567
691
|
|
568
|
-
def add_attributes_to_component(component, change, ignore_keys: [])
|
692
|
+
def add_attributes_to_component(component, change, change_path, ignore_keys: [])
|
569
693
|
change.each do |change_name, change_value|
|
570
694
|
next if (%w[isa displayName] + ignore_keys).include?(change_name)
|
571
695
|
|
572
696
|
attribute_name = attribute_name_from_change_name(change_name)
|
573
697
|
if simple_attribute?(component, attribute_name)
|
574
|
-
|
698
|
+
simple_attribute_change = {
|
699
|
+
added: change_value,
|
700
|
+
removed: simple_attribute_default_value(component, attribute_name)
|
701
|
+
}
|
702
|
+
apply_change_to_simple_attribute(component, attribute_name, simple_attribute_change)
|
575
703
|
next
|
576
704
|
end
|
577
705
|
|
578
706
|
case change_value
|
579
707
|
when Hash
|
580
|
-
add_child_to_component(component, change_value)
|
708
|
+
add_child_to_component(component, change_value, change_path)
|
581
709
|
when Array
|
582
710
|
change_value.each do |added_attribute_element|
|
583
|
-
add_child_to_component(component, added_attribute_element)
|
711
|
+
add_child_to_component(component, added_attribute_element, change_path)
|
584
712
|
end
|
585
713
|
else
|
586
714
|
raise MergeError, "Trying to add attribute of unsupported type '#{change_value.class}' " \
|
@@ -589,16 +717,20 @@ module Kintsugi
|
|
589
717
|
end
|
590
718
|
end
|
591
719
|
|
592
|
-
def
|
720
|
+
def simple_attribute_default_value(component, attribute_name)
|
721
|
+
component.simple_attributes.find do |attribute|
|
722
|
+
attribute.name == attribute_name
|
723
|
+
end.default_value
|
724
|
+
end
|
725
|
+
|
726
|
+
def find_file(project, path, file_filter: ->(_) { true })
|
593
727
|
file_references = project.files.select do |file_reference|
|
594
|
-
file_reference.path ==
|
728
|
+
file_reference.path == path && file_filter.call(file_reference)
|
595
729
|
end
|
596
730
|
if file_references.length > 1
|
597
|
-
puts "Debug: Found more than one matching file with path "
|
598
|
-
"'#{file_reference_change["path"]}'. Using the first one."
|
731
|
+
puts "Debug: Found more than one matching file with path '#{path}'. Using the first one."
|
599
732
|
elsif file_references.empty?
|
600
|
-
puts "Debug: No file reference found for file with path "
|
601
|
-
"'#{file_reference_change["path"]}'."
|
733
|
+
puts "Debug: No file reference found for file with path '#{path}'."
|
602
734
|
return
|
603
735
|
end
|
604
736
|
|