refinement 0.2.1 → 0.4.1

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: 61159414fd746fe91c0a50bf44ad0f382a7abd0b9aae6fd16c67e989f9039650
4
- data.tar.gz: f1d97a4cf3a54cee29209928dc3cbff10f981abc2b925d46b2312a6018ad9064
3
+ metadata.gz: 1d250f8a41cab420b717380a1b0c713541ecda9d0a8439c596bd351c25631fbf
4
+ data.tar.gz: cc6635b7ebb6f797f7d5142f99a7a85b355d90a0a09b5af15f39a5a6450c389c
5
5
  SHA512:
6
- metadata.gz: 7b4c9f79c1971593e745864674fcabb9f05b89a0708e76d8bf59d0f37f9302a488847b2971b2c40d07287efe3d6aab8e23a0e1a95972beaae375cd06a6a55bed
7
- data.tar.gz: ce51b8bc7721049959317f8366fb508e593c0690d1c78f12da13d0a69f2f5c9fd74149f2cfb413a116a51b9a990e74ba8fef192cfc59f20360236570235f1ce8
6
+ metadata.gz: bebda46b92a0bc1eaa25d4852c674a697472caa89f67d9fd5e990d0fd16cd7bf594b0c19559d9a8b4a9de7f4fe7a91f08d6f825ab2116afbe3b6fe5525d42cd1
7
+ data.tar.gz: c86b9b7e206c8b201374817bc4c27b8e7b5372cec0e1a082f27e5c94d34245e50fb237cb5e22c6d3abadb33194ed90b7281d26a69b3484f470f477a47c20879e
@@ -1,5 +1,25 @@
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
+
18
+ ## 0.2.2 (2019-04-08)
19
+
20
+ No changes.
21
+
22
+
3
23
  ## 0.2.1 (2019-04-08)
4
24
 
5
25
  ##### Bug Fixes
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.1
1
+ 0.4.1
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,3 +1,5 @@
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.
@@ -26,12 +28,12 @@ module Refinement
26
28
  @changeset = changeset
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_changeset(changeset)
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
@@ -150,10 +166,11 @@ module Refinement
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
@@ -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
@@ -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
@@ -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
 
@@ -43,6 +45,7 @@ module Refinement
43
45
  dirs = Set.new
44
46
  add = lambda { |path|
45
47
  break unless dirs.add?(path)
48
+
46
49
  add[path.dirname]
47
50
  }
48
51
  modifications.each do |mod|
@@ -124,8 +127,10 @@ module Refinement
124
127
  # @param keypath [Array]
125
128
  def find_modification_for_yaml_keypath(absolute_path:, keypath:)
126
129
  return unless (file_modification = find_modification_for_path(absolute_path: absolute_path))
130
+
127
131
  diff = file_modification.yaml_diff(keypath)
128
132
  return unless diff
133
+
129
134
  [file_modification, diff]
130
135
  end
131
136
 
@@ -144,14 +149,14 @@ module Refinement
144
149
  end
145
150
 
146
151
  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'
152
+ 'was added': 'A',
153
+ 'was copied': 'C',
154
+ 'was deleted': 'D',
155
+ 'was modified': 'M',
156
+ 'was renamed': 'R',
157
+ 'changed type': 'T',
158
+ 'is unmerged': 'U',
159
+ 'changed in an unknown way': 'X'
155
160
  }.freeze
156
161
  private_constant :CHANGE_TYPES
157
162
 
@@ -206,9 +211,8 @@ module Refinement
206
211
  def self.git!(command, *args, chdir:)
207
212
  require 'open3'
208
213
  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
214
+ raise GitError, "Running git #{command} failed (#{status.to_s.gsub(/pid \d+\s*/, '')}):\n\n#{err}" unless status.success?
215
+
212
216
  out
213
217
  end
214
218
  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,
@@ -9,6 +11,7 @@ module Refinement
9
11
  # Initializes a post-install writer with CocoaPods target objects.
10
12
  # @return [CocoaPodsPostInstallWriter] a new instance of CocoaPodsPostInstallWriter
11
13
  # @param aggregate_targets [Array<Pod::AggregateTarget>]
14
+ # @param pod_targets [Array<Pod::PodTarget>]
12
15
  # @param config [Pod::Config]
13
16
  # @param options [Hash]
14
17
  def initialize(aggregate_targets, pod_targets, config, options)
@@ -100,6 +103,10 @@ module Refinement
100
103
  inclusion_reason: 'CocoaPods lockfile',
101
104
  yaml_keypath: ['SPEC CHECKSUMS', pod_target.pod_name] }
102
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
103
110
  spec_paths.each { |path| paths << { path: path, inclusion_reason: 'podspec' } }
104
111
 
105
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
@@ -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.1
4
+ version: 0.4.1
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-04 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: []