refinement 0.2.2 → 0.5.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/CHANGELOG.md +15 -0
- data/VERSION +1 -1
- data/exe/refine +1 -0
- data/lib/cocoapods_plugin.rb +3 -3
- data/lib/refinement.rb +2 -0
- data/lib/refinement/analyzer.rb +65 -41
- data/lib/refinement/annotated_target.rb +10 -3
- data/lib/refinement/changeset.rb +20 -13
- data/lib/refinement/changeset/file_modification.rb +11 -4
- data/lib/refinement/cli.rb +3 -0
- data/lib/refinement/cocoapods_post_install_writer.rb +6 -0
- data/lib/refinement/used_path.rb +49 -9
- data/lib/refinement/version.rb +2 -0
- metadata +8 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3f99f7e0933bae18ddd8c4f6fbd14f3e182f6233a1244b1418ccf678b5c1e471
|
4
|
+
data.tar.gz: 451abebd764ff651e9489095bca0ce0fc4043965e81e962ca49df68e90341367
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6f8348156cedae83212560898d95229be93c592347e052a82bd5a1bd3b13d76a7298703976f4e93799cd8d38543316409462d9c6ef564e1680bdde746bf084aa
|
7
|
+
data.tar.gz: 1109c4b167045b0a2ac2ba45aa036ec6b3d4aacf06726be393c68fe072ee49479d61d41c89a6a29ad927b1e419a3e7932f82acba25ccf02fe4a08efc0a8aaae6
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,20 @@
|
|
1
1
|
# Refinement Changes
|
2
2
|
|
3
|
+
## 0.3.1 (2019-08-09)
|
4
|
+
|
5
|
+
##### Bug Fixes
|
6
|
+
|
7
|
+
* Take into consideration product reference names when refinining.
|
8
|
+
|
9
|
+
|
10
|
+
## 0.3.0 (2019-08-09)
|
11
|
+
|
12
|
+
##### Bug Fixes
|
13
|
+
|
14
|
+
* Multiple YAML used keys paths for the same file will no longer cause the YAML
|
15
|
+
to be parsed multiple times.
|
16
|
+
|
17
|
+
|
3
18
|
## 0.2.2 (2019-04-08)
|
4
19
|
|
5
20
|
No changes.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.5.0
|
data/exe/refine
CHANGED
data/lib/cocoapods_plugin.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'cocoapods'
|
2
4
|
|
3
5
|
Pod::Installer
|
@@ -7,9 +9,7 @@ Pod::Installer
|
|
7
9
|
|
8
10
|
return unless plugins.key?('refinement')
|
9
11
|
|
10
|
-
unless Gem::Version.create(Pod::VERSION) >= Gem::Version.create('1.6.0')
|
11
|
-
raise Pod::Informative, 'Refinement requires a CocoaPods version >= 1.6.0'
|
12
|
-
end
|
12
|
+
raise Pod::Informative, 'Refinement requires a CocoaPods version >= 1.6.0' unless Gem::Version.create(Pod::VERSION) >= Gem::Version.create('1.6.0')
|
13
13
|
|
14
14
|
require 'refinement/cocoapods_post_install_writer'
|
15
15
|
Pod::UI.message 'Writing refinement file' do
|
data/lib/refinement.rb
CHANGED
data/lib/refinement/analyzer.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Refinement
|
2
4
|
# Analyzes changes in a repository
|
3
5
|
# and determines how those changes impact the targets in Xcode projects in the workspace.
|
4
6
|
class Analyzer
|
5
|
-
attr_reader :
|
6
|
-
private :
|
7
|
+
attr_reader :changesets, :workspace_path, :augmenting_paths_yaml_files
|
8
|
+
private :changesets, :workspace_path, :augmenting_paths_yaml_files
|
7
9
|
|
8
|
-
# Initializes an analyzer with
|
9
|
-
# @param
|
10
|
+
# Initializes an analyzer with changesets, projects, and augmenting paths.
|
11
|
+
# @param changesets [Array<Changeset>]
|
10
12
|
# @param workspace_path [Pathname] path to a root workspace or project,
|
11
13
|
# must be `nil` if `projects` are specified explicitly
|
12
14
|
# @param projects [Array<Xcodeproj::Project>] projects to find targets in,
|
@@ -20,18 +22,18 @@ module Refinement
|
|
20
22
|
#
|
21
23
|
# @raise [ArgumentError] when conflicting arguments are given
|
22
24
|
#
|
23
|
-
def initialize(
|
25
|
+
def initialize(changesets:, workspace_path:, projects: nil,
|
24
26
|
augmenting_paths_yaml_files:, augmenting_paths_by_target: nil)
|
25
27
|
|
26
|
-
@
|
28
|
+
@changesets = changesets
|
27
29
|
|
28
30
|
raise ArgumentError, 'Can only specify one of workspace_path and projects' if workspace_path && projects
|
31
|
+
|
29
32
|
@workspace_path = workspace_path
|
30
33
|
@projects = projects
|
31
34
|
|
32
|
-
if augmenting_paths_yaml_files && augmenting_paths_by_target
|
33
|
-
|
34
|
-
end
|
35
|
+
raise ArgumentError, 'Can only specify one of augmenting_paths_yaml_files and augmenting_paths_by_target' if augmenting_paths_yaml_files && augmenting_paths_by_target
|
36
|
+
|
35
37
|
@augmenting_paths_yaml_files = augmenting_paths_yaml_files
|
36
38
|
@augmenting_paths_by_target = augmenting_paths_by_target
|
37
39
|
end
|
@@ -75,25 +77,38 @@ module Refinement
|
|
75
77
|
"Given: #{filter_scheme_for_build_action.inspect}."
|
76
78
|
end
|
77
79
|
|
78
|
-
if filter_when_scheme_has_changed
|
79
|
-
|
80
|
+
if !filter_when_scheme_has_changed &&
|
81
|
+
UsedPath.new(path: Pathname(scheme_path), inclusion_reason: 'scheme').find_in_changesets(changesets)
|
82
|
+
return scheme
|
83
|
+
end
|
84
|
+
|
85
|
+
changes_by_suite_name = Hash[annotate_targets!
|
86
|
+
.map { |at| [at.xcode_target.name, at.change_reason(level: change_level)] }]
|
80
87
|
|
81
|
-
|
82
|
-
.map { |at| [at.xcode_target.name, at.change_reason(level: change_level)] }]
|
88
|
+
doc = scheme.doc
|
83
89
|
|
84
|
-
|
90
|
+
xpaths = sections_to_filter.map { |section| "//*/#{section}/BuildableReference" }
|
91
|
+
xpaths.each do |xpath|
|
92
|
+
doc.get_elements(xpath).to_a.each do |buildable_reference|
|
93
|
+
suite_name = buildable_reference.attributes['BlueprintName']
|
94
|
+
if (change_reason = changes_by_suite_name[suite_name])
|
95
|
+
puts "#{suite_name} changed because #{change_reason}" if log_changes
|
96
|
+
next
|
97
|
+
end
|
98
|
+
puts "#{suite_name} did not change, removing from scheme" if log_changes
|
99
|
+
buildable_reference.parent.remove
|
100
|
+
end
|
101
|
+
end
|
85
102
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
next
|
93
|
-
end
|
94
|
-
puts "#{suite_name} did not change, removing from scheme" if log_changes
|
95
|
-
buildable_reference.parent.remove
|
103
|
+
if filter_scheme_for_build_action == :testing
|
104
|
+
doc.get_elements('//*/BuildActionEntry/BuildableReference').to_a.each do |buildable_reference|
|
105
|
+
suite_name = buildable_reference.attributes['BlueprintName']
|
106
|
+
if (change_reason = changes_by_suite_name[suite_name])
|
107
|
+
puts "#{suite_name} changed because #{change_reason}" if log_changes
|
108
|
+
next
|
96
109
|
end
|
110
|
+
puts "#{suite_name} did not change, setting to not build for testing" if log_changes
|
111
|
+
buildable_reference.parent.attributes['buildForTesting'] = 'NO'
|
97
112
|
end
|
98
113
|
end
|
99
114
|
|
@@ -110,6 +125,7 @@ module Refinement
|
|
110
125
|
.map do |annotated_target|
|
111
126
|
change_reason = annotated_target.change_reason(level: change_level)
|
112
127
|
next if !include_unchanged_targets && !change_reason
|
128
|
+
|
113
129
|
change_reason ||= 'did not change'
|
114
130
|
"\t#{annotated_target.xcode_target}: #{change_reason}"
|
115
131
|
end.compact
|
@@ -129,7 +145,7 @@ module Refinement
|
|
129
145
|
@augmenting_paths_by_target ||= begin
|
130
146
|
require 'yaml'
|
131
147
|
augmenting_paths_yaml_files.reduce({}) do |augmenting_paths_by_target, yaml_file|
|
132
|
-
yaml_file = Pathname(yaml_file).expand_path(
|
148
|
+
yaml_file = Pathname(yaml_file).expand_path(changesets.first.repository)
|
133
149
|
yaml = YAML.safe_load(yaml_file.read)
|
134
150
|
augmenting_paths_by_target.merge(yaml) do |_target_name, prior_paths, new_paths|
|
135
151
|
prior_paths + new_paths
|
@@ -141,19 +157,20 @@ module Refinement
|
|
141
157
|
# @return [Array<AnnotatedTarget>] targets in the given list of Xcode projects,
|
142
158
|
# annotated according to the given changeset
|
143
159
|
def annotated_targets
|
144
|
-
workspace_modification =
|
160
|
+
workspace_modification = find_workspace_modification_in_changesets
|
145
161
|
project_changes = Hash[projects.map do |project|
|
146
|
-
[project,
|
162
|
+
[project, find_project_modification_in_changesets(project: project) || workspace_modification]
|
147
163
|
end]
|
148
164
|
|
149
165
|
require 'tsort'
|
150
166
|
targets = projects.flat_map(&:targets)
|
151
167
|
targets_by_uuid = Hash[targets.map { |t| [t.uuid, t] }]
|
152
168
|
targets_by_name = Hash[targets.map { |t| [t.name, t] }]
|
153
|
-
targets_by_product_name =
|
169
|
+
targets_by_product_name = targets.each_with_object({}) do |t, h|
|
154
170
|
next unless t.respond_to?(:product_reference)
|
155
|
-
[File.basename(t.product_reference.path)
|
156
|
-
|
171
|
+
h[File.basename(t.product_reference.path)] = t
|
172
|
+
h[File.basename(t.product_reference.name)] = t if t.product_reference.name
|
173
|
+
end
|
157
174
|
|
158
175
|
find_dep = ->(td) { targets_by_uuid[td.native_target_uuid] || targets_by_name[td.name] }
|
159
176
|
target_deps = lambda do |target|
|
@@ -166,7 +183,7 @@ module Refinement
|
|
166
183
|
# yay auto-linking
|
167
184
|
if (phase = target.frameworks_build_phases)
|
168
185
|
phase.files_references.each do |fr|
|
169
|
-
if (dt = fr
|
186
|
+
if (dt = fr&.path && targets_by_product_name[File.basename(fr.path)])
|
170
187
|
target_dependencies << dt
|
171
188
|
end
|
172
189
|
end
|
@@ -181,7 +198,7 @@ module Refinement
|
|
181
198
|
)
|
182
199
|
|
183
200
|
targets.each_with_object({}) do |target, h|
|
184
|
-
change_reason = project_changes[target.project] ||
|
201
|
+
change_reason = project_changes[target.project] || find_target_modification_in_changesets(target: target)
|
185
202
|
|
186
203
|
h[target] = AnnotatedTarget.new(
|
187
204
|
target: target,
|
@@ -232,6 +249,7 @@ module Refinement
|
|
232
249
|
|
233
250
|
expand_build_settings = lambda do |s|
|
234
251
|
return [s] unless s =~ /\$(?:\{([_a-zA-Z0-0]+?)\}|\(([_a-zA-Z0-0]+?)\))/
|
252
|
+
|
235
253
|
match, key = Regexp.last_match.values_at(0, 1, 2).compact
|
236
254
|
substitutions = target.resolved_build_setting(key, true).values.compact.uniq
|
237
255
|
substitutions.flat_map do |sub|
|
@@ -242,6 +260,7 @@ module Refinement
|
|
242
260
|
target.build_configuration_list.build_configurations.each do |build_configuration|
|
243
261
|
ref = build_configuration.base_configuration_reference
|
244
262
|
next unless ref
|
263
|
+
|
245
264
|
yield UsedPath.new(path: ref.real_path,
|
246
265
|
inclusion_reason: "base configuration reference for #{build_configuration}")
|
247
266
|
end
|
@@ -249,6 +268,7 @@ module Refinement
|
|
249
268
|
target.build_phases.each do |build_phase|
|
250
269
|
build_phase.files_references.each do |fr|
|
251
270
|
next unless fr
|
271
|
+
|
252
272
|
yield UsedPath.new(path: fr.real_path,
|
253
273
|
inclusion_reason: "#{build_phase.display_name.downcase.chomp('s')} file")
|
254
274
|
end
|
@@ -257,9 +277,11 @@ module Refinement
|
|
257
277
|
target.shell_script_build_phases.each do |shell_script_build_phase|
|
258
278
|
%w[input_file_list_paths output_file_list_paths input_paths output_paths].each do |method|
|
259
279
|
next unless (paths = shell_script_build_phase.public_send(method))
|
280
|
+
|
260
281
|
file_type = method.tr('_', ' ').chomp('s')
|
261
282
|
paths.each do |config_path|
|
262
283
|
next unless config_path
|
284
|
+
|
263
285
|
expand_build_settings[config_path].each do |path|
|
264
286
|
path = Pathname(path).expand_path(target.project.project_dir)
|
265
287
|
yield UsedPath.new(path: path,
|
@@ -273,6 +295,7 @@ module Refinement
|
|
273
295
|
target.resolved_build_setting(build_setting, true).each_value do |paths|
|
274
296
|
Array(paths).each do |path|
|
275
297
|
next unless path
|
298
|
+
|
276
299
|
path = Pathname(path).expand_path(target.project.project_dir)
|
277
300
|
yield UsedPath.new(path: path, inclusion_reason: "#{build_setting} value")
|
278
301
|
end
|
@@ -283,11 +306,11 @@ module Refinement
|
|
283
306
|
# @return [FileModification,Nil] a modification to a file that is used by the given target, or `nil`
|
284
307
|
# if none if found
|
285
308
|
# @param target [Xcodeproj::Project::AbstractTarget]
|
286
|
-
def
|
309
|
+
def find_target_modification_in_changesets(target:)
|
287
310
|
augmenting_paths = used_paths_from_augmenting_paths_by_target[target.name]
|
288
|
-
|
289
|
-
Refinement.map_find(augmenting_paths, &
|
290
|
-
Refinement.map_find(target_each_file_path(target: target), &
|
311
|
+
find_in_changesets = ->(path) { path.find_in_changesets(changesets) }
|
312
|
+
Refinement.map_find(augmenting_paths, &find_in_changesets) ||
|
313
|
+
Refinement.map_find(target_each_file_path(target: target), &find_in_changesets)
|
291
314
|
end
|
292
315
|
|
293
316
|
# @yieldparam used_path [UsedPath] an absolute path that belongs to the given project
|
@@ -301,6 +324,7 @@ module Refinement
|
|
301
324
|
project.root_object.build_configuration_list.build_configurations.each do |build_configuration|
|
302
325
|
ref = build_configuration.base_configuration_reference
|
303
326
|
next unless ref
|
327
|
+
|
304
328
|
yield UsedPath.new(path: ref.real_path,
|
305
329
|
inclusion_reason: "base configuration reference for #{build_configuration}")
|
306
330
|
end
|
@@ -310,9 +334,9 @@ module Refinement
|
|
310
334
|
# if none if found
|
311
335
|
# @note This method does not take into account whatever file paths targets in the project may reference
|
312
336
|
# @param project [Xcodeproj::Project]
|
313
|
-
def
|
337
|
+
def find_project_modification_in_changesets(project:)
|
314
338
|
Refinement.map_find(project_each_file_path(project: project)) do |path|
|
315
|
-
path.
|
339
|
+
path.find_in_changesets(changesets)
|
316
340
|
end
|
317
341
|
end
|
318
342
|
|
@@ -320,17 +344,17 @@ module Refinement
|
|
320
344
|
# if none if found
|
321
345
|
# @note This method does not take into account whatever file paths projects or
|
322
346
|
# targets in the workspace path may reference
|
323
|
-
def
|
347
|
+
def find_workspace_modification_in_changesets
|
324
348
|
return unless workspace_path
|
325
349
|
|
326
350
|
UsedPath.new(path: workspace_path, inclusion_reason: 'workspace directory')
|
327
|
-
.
|
351
|
+
.find_in_changesets(changesets)
|
328
352
|
end
|
329
353
|
|
330
354
|
# @return [Hash<String,UsedPath>]
|
331
355
|
def used_paths_from_augmenting_paths_by_target
|
332
356
|
@used_paths_from_augmenting_paths_by_target ||= begin
|
333
|
-
repo =
|
357
|
+
repo = changesets.first.repository
|
334
358
|
used_paths_from_augmenting_paths_by_target =
|
335
359
|
augmenting_paths_by_target.each_with_object({}) do |(name, augmenting_paths), h|
|
336
360
|
h[name] = augmenting_paths.map do |augmenting_path|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Refinement
|
2
4
|
# A target, annotated with any changes
|
3
5
|
class AnnotatedTarget
|
@@ -45,15 +47,20 @@ module Refinement
|
|
45
47
|
when :full_transitive
|
46
48
|
direct_change_reason || Refinement.map_find(dependencies) do |dependency|
|
47
49
|
next unless (dependency_change_reason = dependency.change_reason(level: level))
|
50
|
+
|
48
51
|
"dependency #{dependency} changed because #{dependency_change_reason}"
|
49
52
|
end
|
50
53
|
when proc { |symbol, int| (symbol == :at_most_n_away) && int.is_a?(Integer) }
|
51
54
|
distance_from_target = level.last
|
52
|
-
raise ArgumentError, "level must be positive, not #{distance_from_target}" if distance_from_target
|
55
|
+
raise ArgumentError, "level must be positive, not #{distance_from_target}" if distance_from_target.negative?
|
56
|
+
|
53
57
|
change_reason = direct_change_reason
|
54
|
-
if distance_from_target
|
58
|
+
if distance_from_target.positive?
|
55
59
|
change_reason ||= Refinement.map_find(dependencies) do |dependency|
|
56
|
-
|
60
|
+
unless (dependency_change_reason = dependency.change_reason(level: [:at_most_n_away, level.last.pred]))
|
61
|
+
next
|
62
|
+
end
|
63
|
+
|
57
64
|
"dependency #{dependency} changed because #{dependency_change_reason}"
|
58
65
|
end
|
59
66
|
end
|
data/lib/refinement/changeset.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'cocoapods/executable'
|
2
4
|
require 'set'
|
3
5
|
|
@@ -16,12 +18,15 @@ module Refinement
|
|
16
18
|
attr_reader :modified_paths
|
17
19
|
# @return [Hash<Pathname,FileModification>] modifications keyed by relative path
|
18
20
|
attr_reader :modified_absolute_paths
|
21
|
+
# @return [String] a desciption of the changeset
|
22
|
+
attr_reader :description
|
19
23
|
|
20
24
|
private :modifications, :modified_paths, :modified_absolute_paths
|
21
25
|
|
22
|
-
def initialize(repository:, modifications:)
|
26
|
+
def initialize(repository:, modifications:, description: nil)
|
23
27
|
@repository = repository
|
24
28
|
@modifications = self.class.add_directories(modifications).uniq.freeze
|
29
|
+
@description = description
|
25
30
|
|
26
31
|
@modified_paths = {}
|
27
32
|
@modifications
|
@@ -43,6 +48,7 @@ module Refinement
|
|
43
48
|
dirs = Set.new
|
44
49
|
add = lambda { |path|
|
45
50
|
break unless dirs.add?(path)
|
51
|
+
|
46
52
|
add[path.dirname]
|
47
53
|
}
|
48
54
|
modifications.each do |mod|
|
@@ -124,8 +130,10 @@ module Refinement
|
|
124
130
|
# @param keypath [Array]
|
125
131
|
def find_modification_for_yaml_keypath(absolute_path:, keypath:)
|
126
132
|
return unless (file_modification = find_modification_for_path(absolute_path: absolute_path))
|
133
|
+
|
127
134
|
diff = file_modification.yaml_diff(keypath)
|
128
135
|
return unless diff
|
136
|
+
|
129
137
|
[file_modification, diff]
|
130
138
|
end
|
131
139
|
|
@@ -140,18 +148,18 @@ module Refinement
|
|
140
148
|
diff = git!('diff', '--raw', '-z', merge_base, chdir: repository)
|
141
149
|
modifications = parse_raw_diff(diff, repository: repository, base_revision: merge_base).freeze
|
142
150
|
|
143
|
-
new(repository: repository, modifications: modifications)
|
151
|
+
new(repository: repository, modifications: modifications, description: "since #{base_revision}")
|
144
152
|
end
|
145
153
|
|
146
154
|
CHANGE_TYPES = {
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
+
'was added': 'A',
|
156
|
+
'was copied': 'C',
|
157
|
+
'was deleted': 'D',
|
158
|
+
'was modified': 'M',
|
159
|
+
'was renamed': 'R',
|
160
|
+
'changed type': 'T',
|
161
|
+
'is unmerged': 'U',
|
162
|
+
'changed in an unknown way': 'X'
|
155
163
|
}.freeze
|
156
164
|
private_constant :CHANGE_TYPES
|
157
165
|
|
@@ -206,9 +214,8 @@ module Refinement
|
|
206
214
|
def self.git!(command, *args, chdir:)
|
207
215
|
require 'open3'
|
208
216
|
out, err, status = Open3.capture3('git', command, *args, chdir: chdir.to_s)
|
209
|
-
unless status.success?
|
210
|
-
|
211
|
-
end
|
217
|
+
raise GitError, "Running git #{command} failed (#{status.to_s.gsub(/pid \d+\s*/, '')}):\n\n#{err}" unless status.success?
|
218
|
+
|
212
219
|
out
|
213
220
|
end
|
214
221
|
private_class_method :git!
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Refinement
|
2
4
|
class Changeset
|
3
5
|
# Represents a modification to a single file or directory on disk
|
@@ -51,12 +53,14 @@ module Refinement
|
|
51
53
|
# @visibility private
|
52
54
|
def ==(other)
|
53
55
|
return unless other.is_a?(FileModification)
|
56
|
+
|
54
57
|
(path == other.path) && (type == other.type) && prior_path == other.prior_path
|
55
58
|
end
|
56
59
|
|
57
60
|
# @visibility private
|
58
61
|
def eql?(other)
|
59
62
|
return unless other.is_a?(FileModification)
|
63
|
+
|
60
64
|
path.eql?(other.path) && type.eql?(other.type) && prior_path.eql?(other.prior_path)
|
61
65
|
end
|
62
66
|
|
@@ -68,9 +72,12 @@ module Refinement
|
|
68
72
|
def yaml_diff(keypath)
|
69
73
|
require 'yaml'
|
70
74
|
|
71
|
-
|
75
|
+
@cached_yaml ||= {}
|
76
|
+
|
77
|
+
dig_yaml = lambda do |yaml, path|
|
72
78
|
return yaml if DOES_NOT_EXIST == yaml
|
73
|
-
|
79
|
+
|
80
|
+
object = @cached_yaml[path] ||= YAML.safe_load(yaml, [Symbol])
|
74
81
|
if keypath.empty?
|
75
82
|
object
|
76
83
|
elsif object.respond_to?(:dig)
|
@@ -82,8 +89,8 @@ module Refinement
|
|
82
89
|
end
|
83
90
|
end
|
84
91
|
|
85
|
-
prior = dig_yaml[prior_contents]
|
86
|
-
current = dig_yaml[contents]
|
92
|
+
prior = dig_yaml[prior_contents, :prior]
|
93
|
+
current = dig_yaml[contents, :current]
|
87
94
|
|
88
95
|
require 'xcodeproj/differ'
|
89
96
|
|
data/lib/refinement/cli.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'claide'
|
2
4
|
|
3
5
|
module Refinement
|
@@ -48,6 +50,7 @@ module Refinement
|
|
48
50
|
puts analyzer.format_changes if @print_changes
|
49
51
|
|
50
52
|
return unless @scheme
|
53
|
+
|
51
54
|
analyzer.filtered_scheme(scheme_path: @scheme, log_changes: @print_scheme_changes, filter_scheme_for_build_action: @filter_scheme_for_build_action)
|
52
55
|
.save_as(@scheme.gsub(%r{\.(xcodeproj|xcworkspace)/.+}, '.\1'), File.basename(@scheme, '.xcscheme'), true)
|
53
56
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Refinement
|
2
4
|
# Called after CocoaPods installation to write an augmenting file that
|
3
5
|
# takes into account changes to Pod configuration,
|
@@ -101,6 +103,10 @@ module Refinement
|
|
101
103
|
inclusion_reason: 'CocoaPods lockfile',
|
102
104
|
yaml_keypath: ['SPEC CHECKSUMS', pod_target.pod_name] }
|
103
105
|
]
|
106
|
+
if pod_target.sandbox.predownloaded?(pod_target.pod_name)
|
107
|
+
paths << { path: 'Podfile.lock', inclusion_reason: 'Dependency external source', yaml_keypath: ['EXTERNAL SOURCES', pod_target.pod_name] }
|
108
|
+
paths << { path: 'Podfile.lock', inclusion_reason: 'Pod checkout options', yaml_keypath: ['CHECKOUT OPTIONS', pod_target.pod_name] }
|
109
|
+
end
|
104
110
|
spec_paths.each { |path| paths << { path: path, inclusion_reason: 'podspec' } }
|
105
111
|
|
106
112
|
Pod::Validator::FILE_PATTERNS.each do |pattern|
|
data/lib/refinement/used_path.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Refinement
|
2
4
|
# Represents a path that some target depends upon.
|
3
5
|
class UsedPath
|
@@ -17,7 +19,17 @@ module Refinement
|
|
17
19
|
# @return [Nil, String] If the path has been modified, a string explaining the modification
|
18
20
|
# @param changeset [Changeset] the changeset to search for a modification to this path
|
19
21
|
def find_in_changeset(changeset)
|
20
|
-
add_reason changeset.find_modification_for_path(absolute_path: path)
|
22
|
+
add_reason changeset.find_modification_for_path(absolute_path: path), changeset: changeset
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [Nil, String] If the path has been modified, a string explaining the modification
|
26
|
+
# @param changesets [Array<Changeset>] the changesets to search for a modification to this path
|
27
|
+
def find_in_changesets(changesets)
|
28
|
+
raise ArgumentError, 'Must provide at least one changeset' if changesets.empty?
|
29
|
+
|
30
|
+
changesets.reduce(true) do |explanation, changeset|
|
31
|
+
explanation && find_in_changeset(changeset)
|
32
|
+
end
|
21
33
|
end
|
22
34
|
|
23
35
|
# @return [String]
|
@@ -31,10 +43,22 @@ module Refinement
|
|
31
43
|
# @return [Nil, String] A string suitable for user display that explains
|
32
44
|
# why the given modification means a target is modified
|
33
45
|
# @param modification [Nil, FileModification]
|
34
|
-
|
46
|
+
# @param changeset [Changeset]
|
47
|
+
def add_reason(modification, changeset:)
|
35
48
|
return unless modification
|
36
49
|
|
37
|
-
"#{modification.path} (#{inclusion_reason}) #{modification.type}"
|
50
|
+
add_changeset_description "#{modification.path} (#{inclusion_reason}) #{modification.type}", changeset: changeset
|
51
|
+
end
|
52
|
+
|
53
|
+
# @return [String] A string suitable for user display that explains
|
54
|
+
# why the given modification means a target is modified, including the description
|
55
|
+
# of the changeset that contains the modification
|
56
|
+
# @param description [String]
|
57
|
+
# @param changeset [Nil, Changeset]
|
58
|
+
def add_changeset_description(description, changeset:)
|
59
|
+
return description unless changeset&.description
|
60
|
+
|
61
|
+
description + " (#{changeset.description})"
|
38
62
|
end
|
39
63
|
|
40
64
|
# Represents a path to a YAML file that some target depends upon,
|
@@ -52,7 +76,7 @@ module Refinement
|
|
52
76
|
# (see UsedPath#find_in_changeset)
|
53
77
|
def find_in_changeset(changeset)
|
54
78
|
modification, _yaml_diff = changeset.find_modification_for_yaml_keypath(absolute_path: path, keypath: yaml_keypath)
|
55
|
-
add_reason modification
|
79
|
+
add_reason modification, changeset: changeset
|
56
80
|
end
|
57
81
|
|
58
82
|
# (see UsedPath#to_s)
|
@@ -63,7 +87,7 @@ module Refinement
|
|
63
87
|
private
|
64
88
|
|
65
89
|
# (see UsedPath#add_reason)
|
66
|
-
def add_reason(modification)
|
90
|
+
def add_reason(modification, changeset:)
|
67
91
|
return unless modification
|
68
92
|
|
69
93
|
keypath_string =
|
@@ -72,7 +96,7 @@ module Refinement
|
|
72
96
|
else
|
73
97
|
' @ ' + yaml_keypath.map { |path| path.to_s =~ /\A[a-zA-Z0-9_]+\z/ ? path : path.inspect }.join('.')
|
74
98
|
end
|
75
|
-
"#{modification.path}#{keypath_string} (#{inclusion_reason}) #{modification.type}"
|
99
|
+
add_changeset_description "#{modification.path}#{keypath_string} (#{inclusion_reason}) #{modification.type}", changeset: changeset
|
76
100
|
end
|
77
101
|
end
|
78
102
|
end
|
@@ -94,7 +118,16 @@ module Refinement
|
|
94
118
|
|
95
119
|
# (see UsedPath#find_in_changeset)
|
96
120
|
def find_in_changeset(changeset)
|
97
|
-
add_reason changeset.find_modification_for_glob(absolute_glob: glob)
|
121
|
+
add_reason changeset.find_modification_for_glob(absolute_glob: glob), changeset: changeset
|
122
|
+
end
|
123
|
+
|
124
|
+
# (see UsedPath#find_in_changesets)
|
125
|
+
def find_in_changesets(changesets)
|
126
|
+
raise ArgumentError, 'Must provide at least one changeset' if changesets.empty?
|
127
|
+
|
128
|
+
changesets.reduce(true) do |explanation, changeset|
|
129
|
+
explanation && find_in_changeset(changeset)
|
130
|
+
end
|
98
131
|
end
|
99
132
|
|
100
133
|
# (see UsedPath#to_s)
|
@@ -105,10 +138,17 @@ module Refinement
|
|
105
138
|
private
|
106
139
|
|
107
140
|
# (see UsedPath#add_reason)
|
108
|
-
def add_reason(modification)
|
141
|
+
def add_reason(modification, changeset:)
|
109
142
|
return unless modification
|
110
143
|
|
111
|
-
"#{modification.path} (#{inclusion_reason}) #{modification.type}"
|
144
|
+
add_changeset_description "#{modification.path} (#{inclusion_reason}) #{modification.type}", changeset: changeset
|
145
|
+
end
|
146
|
+
|
147
|
+
# (see UsedPath#add_changeset_description)
|
148
|
+
def add_changeset_description(description, changeset:)
|
149
|
+
return description unless changeset&.description
|
150
|
+
|
151
|
+
description + " (#{changeset.description})"
|
112
152
|
end
|
113
153
|
end
|
114
154
|
end
|
data/lib/refinement/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: refinement
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Giddins
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-08-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: xcodeproj
|
@@ -44,7 +44,7 @@ dependencies:
|
|
44
44
|
- - "~>"
|
45
45
|
- !ruby/object:Gem::Version
|
46
46
|
version: '10.0'
|
47
|
-
description:
|
47
|
+
description:
|
48
48
|
email:
|
49
49
|
- segiddins@squareup.com
|
50
50
|
executables:
|
@@ -70,7 +70,7 @@ files:
|
|
70
70
|
homepage: https://github.com/square/refinement
|
71
71
|
licenses: []
|
72
72
|
metadata: {}
|
73
|
-
post_install_message:
|
73
|
+
post_install_message:
|
74
74
|
rdoc_options: []
|
75
75
|
require_paths:
|
76
76
|
- lib
|
@@ -78,15 +78,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
78
78
|
requirements:
|
79
79
|
- - ">="
|
80
80
|
- !ruby/object:Gem::Version
|
81
|
-
version: '2.
|
81
|
+
version: '2.3'
|
82
82
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
83
|
requirements:
|
84
84
|
- - ">="
|
85
85
|
- !ruby/object:Gem::Version
|
86
86
|
version: '0'
|
87
87
|
requirements: []
|
88
|
-
rubygems_version: 3.0.
|
89
|
-
signing_key:
|
88
|
+
rubygems_version: 3.0.1
|
89
|
+
signing_key:
|
90
90
|
specification_version: 4
|
91
91
|
summary: Generates a list of Xcode targets to build & test as a result of a git diff.
|
92
92
|
test_files: []
|