refinement 0.2.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cde77a0d0d41bfeebca40808c3053d38d9eff4ec7d09d82a9f29d8f9f3bed31b
4
- data.tar.gz: 6d38e9cb3025c970513d80b18074e37eec4ad8edfec278c8c8eab9a4258310e8
3
+ metadata.gz: 3f99f7e0933bae18ddd8c4f6fbd14f3e182f6233a1244b1418ccf678b5c1e471
4
+ data.tar.gz: 451abebd764ff651e9489095bca0ce0fc4043965e81e962ca49df68e90341367
5
5
  SHA512:
6
- metadata.gz: a878d3fc86e0c733da2fcbddc9da9a869ef56e035172e17b8ea63ada49cde2cceb4ead3c5d6b1024343ebd809368f096a77d50cd6464233c87e6d724cac33127
7
- data.tar.gz: '09bd2a181cc8646d2f5a352a5b70bcf41681432a31588ee30502e40416f2641158fcaf59a3a8ad99b52abac383f1494f1804aaacaa97e9e286886fc4edc8afbb'
6
+ metadata.gz: 6f8348156cedae83212560898d95229be93c592347e052a82bd5a1bd3b13d76a7298703976f4e93799cd8d38543316409462d9c6ef564e1680bdde746bf084aa
7
+ data.tar.gz: 1109c4b167045b0a2ac2ba45aa036ec6b3d4aacf06726be393c68fe072ee49479d61d41c89a6a29ad927b1e419a3e7932f82acba25ccf02fe4a08efc0a8aaae6
@@ -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.2.2
1
+ 0.5.0
data/exe/refine CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'refinement'
4
5
  require 'refinement/cli'
@@ -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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'xcodeproj'
2
4
 
3
5
  # Generates a list of Xcode targets to build & test as a result of a git diff.
@@ -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 :changeset, :workspace_path, :augmenting_paths_yaml_files
6
- private :changeset, :workspace_path, :augmenting_paths_yaml_files
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 a changeset, projects, and augmenting paths.
9
- # @param changeset [Changeset]
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(changeset:, workspace_path:, projects: nil,
25
+ def initialize(changesets:, workspace_path:, projects: nil,
24
26
  augmenting_paths_yaml_files:, augmenting_paths_by_target: nil)
25
27
 
26
- @changeset = changeset
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
- raise ArgumentError, 'Can only specify one of augmenting_paths_yaml_files and augmenting_paths_by_target'
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
- !UsedPath.new(path: Pathname(scheme_path), inclusion_reason: 'scheme').find_in_changeset(changeset)
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
- changes_by_suite_name = Hash[annotate_targets!
82
- .map { |at| [at.xcode_target.name, at.change_reason(level: change_level)] }]
88
+ doc = scheme.doc
83
89
 
84
- doc = scheme.doc
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
- xpaths = sections_to_filter.map { |section| "//*/#{section}/BuildableReference" }
87
- xpaths.each do |xpath|
88
- doc.get_elements(xpath).to_a.each do |buildable_reference|
89
- suite_name = buildable_reference.attributes['BlueprintName']
90
- if (change_reason = changes_by_suite_name[suite_name])
91
- puts "#{suite_name} changed because #{change_reason}" if log_changes
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(changeset.repository)
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 = find_workspace_modification_in_changeset
160
+ workspace_modification = find_workspace_modification_in_changesets
145
161
  project_changes = Hash[projects.map do |project|
146
- [project, find_project_modification_in_changeset(project: project) || workspace_modification]
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 = Hash[targets.map do |t|
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), t]
156
- end.compact]
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 && fr.path && targets_by_product_name[File.basename(fr.path)])
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] || find_target_modification_in_changeset(target: target)
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 find_target_modification_in_changeset(target:)
309
+ def find_target_modification_in_changesets(target:)
287
310
  augmenting_paths = used_paths_from_augmenting_paths_by_target[target.name]
288
- find_in_changeset = ->(path) { path.find_in_changeset(changeset) }
289
- Refinement.map_find(augmenting_paths, &find_in_changeset) ||
290
- Refinement.map_find(target_each_file_path(target: target), &find_in_changeset)
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 find_project_modification_in_changeset(project:)
337
+ def find_project_modification_in_changesets(project:)
314
338
  Refinement.map_find(project_each_file_path(project: project)) do |path|
315
- path.find_in_changeset(changeset)
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 find_workspace_modification_in_changeset
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
- .find_in_changeset(changeset)
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 = changeset.repository
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 < 0
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 > 0
58
+ if distance_from_target.positive?
55
59
  change_reason ||= Refinement.map_find(dependencies) do |dependency|
56
- next unless (dependency_change_reason = dependency.change_reason(level: [:at_most_n_away, level.last.pred]))
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
@@ -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
- :'was added' => 'A',
148
- :'was copied' => 'C',
149
- :'was deleted' => 'D',
150
- :'was modified' => 'M',
151
- :'was renamed' => 'R',
152
- :'changed type' => 'T',
153
- :'is unmerged' => 'U',
154
- :'changed in an unknown way' => 'X'
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
- raise GitError, "Running git #{command} failed (#{status.to_s.gsub(/pid \d+\s*/, '')}):\n\n#{err}"
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
- dig_yaml = lambda do |yaml|
75
+ @cached_yaml ||= {}
76
+
77
+ dig_yaml = lambda do |yaml, path|
72
78
  return yaml if DOES_NOT_EXIST == yaml
73
- object = YAML.safe_load(yaml, [Symbol])
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
 
@@ -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|
@@ -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
- def add_reason(modification)
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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Refinement
2
4
  # @visibility private
3
5
  VERSION = File.read(File.expand_path('../../VERSION', __dir__)).strip.freeze
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.2.2
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: 2019-05-09 00:00:00.000000000 Z
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.1'
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.2
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: []