refinement 0.2.1 → 0.4.1

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