kintsugi 0.4.3 → 0.5.3
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/.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
|
|