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 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: []