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 +4 -4
- data/CHANGELOG.md +20 -0
- data/VERSION +1 -1
- data/exe/refine +1 -0
- data/lib/cocoapods_plugin.rb +3 -3
- data/lib/refinement.rb +2 -0
- data/lib/refinement/analyzer.rb +46 -22
- data/lib/refinement/annotated_target.rb +10 -3
- data/lib/refinement/changeset.rb +15 -11
- data/lib/refinement/changeset/file_modification.rb +11 -4
- data/lib/refinement/cli.rb +3 -0
- data/lib/refinement/cocoapods_post_install_writer.rb +7 -0
- data/lib/refinement/used_path.rb +2 -0
- data/lib/refinement/version.rb +2 -0
- metadata +8 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1d250f8a41cab420b717380a1b0c713541ecda9d0a8439c596bd351c25631fbf
|
4
|
+
data.tar.gz: cc6635b7ebb6f797f7d5142f99a7a85b355d90a0a09b5af15f39a5a6450c389c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bebda46b92a0bc1eaa25d4852c674a697472caa89f67d9fd5e990d0fd16cd7bf594b0c19559d9a8b4a9de7f4fe7a91f08d6f825ab2116afbe3b6fe5525d42cd1
|
7
|
+
data.tar.gz: c86b9b7e206c8b201374817bc4c27b8e7b5372cec0e1a082f27e5c94d34245e50fb237cb5e22c6d3abadb33194ed90b7281d26a69b3484f470f477a47c20879e
|
data/CHANGELOG.md
CHANGED
@@ -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.
|
1
|
+
0.4.1
|
data/exe/refine
CHANGED
data/lib/cocoapods_plugin.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'cocoapods'
|
2
4
|
|
3
5
|
Pod::Installer
|
@@ -7,9 +9,7 @@ Pod::Installer
|
|
7
9
|
|
8
10
|
return unless plugins.key?('refinement')
|
9
11
|
|
10
|
-
unless Gem::Version.create(Pod::VERSION) >= Gem::Version.create('1.6.0')
|
11
|
-
raise Pod::Informative, 'Refinement requires a CocoaPods version >= 1.6.0'
|
12
|
-
end
|
12
|
+
raise Pod::Informative, 'Refinement requires a CocoaPods version >= 1.6.0' unless Gem::Version.create(Pod::VERSION) >= Gem::Version.create('1.6.0')
|
13
13
|
|
14
14
|
require 'refinement/cocoapods_post_install_writer'
|
15
15
|
Pod::UI.message 'Writing refinement file' do
|
data/lib/refinement.rb
CHANGED
data/lib/refinement/analyzer.rb
CHANGED
@@ -1,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
|
-
|
34
|
-
end
|
35
|
+
raise ArgumentError, 'Can only specify one of augmenting_paths_yaml_files and augmenting_paths_by_target' if augmenting_paths_yaml_files && augmenting_paths_by_target
|
36
|
+
|
35
37
|
@augmenting_paths_yaml_files = augmenting_paths_yaml_files
|
36
38
|
@augmenting_paths_by_target = augmenting_paths_by_target
|
37
39
|
end
|
@@ -75,25 +77,38 @@ module Refinement
|
|
75
77
|
"Given: #{filter_scheme_for_build_action.inspect}."
|
76
78
|
end
|
77
79
|
|
78
|
-
if filter_when_scheme_has_changed
|
79
|
-
|
80
|
+
if !filter_when_scheme_has_changed &&
|
81
|
+
UsedPath.new(path: Pathname(scheme_path), inclusion_reason: 'scheme').find_in_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
|
-
|
82
|
-
.map { |at| [at.xcode_target.name, at.change_reason(level: change_level)] }]
|
88
|
+
doc = scheme.doc
|
83
89
|
|
84
|
-
|
90
|
+
xpaths = sections_to_filter.map { |section| "//*/#{section}/BuildableReference" }
|
91
|
+
xpaths.each do |xpath|
|
92
|
+
doc.get_elements(xpath).to_a.each do |buildable_reference|
|
93
|
+
suite_name = buildable_reference.attributes['BlueprintName']
|
94
|
+
if (change_reason = changes_by_suite_name[suite_name])
|
95
|
+
puts "#{suite_name} changed because #{change_reason}" if log_changes
|
96
|
+
next
|
97
|
+
end
|
98
|
+
puts "#{suite_name} did not change, removing from scheme" if log_changes
|
99
|
+
buildable_reference.parent.remove
|
100
|
+
end
|
101
|
+
end
|
85
102
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
next
|
93
|
-
end
|
94
|
-
puts "#{suite_name} did not change, removing from scheme" if log_changes
|
95
|
-
buildable_reference.parent.remove
|
103
|
+
if filter_scheme_for_build_action == :testing
|
104
|
+
doc.get_elements('//*/BuildActionEntry/BuildableReference').to_a.each do |buildable_reference|
|
105
|
+
suite_name = buildable_reference.attributes['BlueprintName']
|
106
|
+
if (change_reason = changes_by_suite_name[suite_name])
|
107
|
+
puts "#{suite_name} changed because #{change_reason}" if log_changes
|
108
|
+
next
|
96
109
|
end
|
110
|
+
puts "#{suite_name} did not change, setting to not build for testing" if log_changes
|
111
|
+
buildable_reference.parent.attributes['buildForTesting'] = 'NO'
|
97
112
|
end
|
98
113
|
end
|
99
114
|
|
@@ -110,6 +125,7 @@ module Refinement
|
|
110
125
|
.map do |annotated_target|
|
111
126
|
change_reason = annotated_target.change_reason(level: change_level)
|
112
127
|
next if !include_unchanged_targets && !change_reason
|
128
|
+
|
113
129
|
change_reason ||= 'did not change'
|
114
130
|
"\t#{annotated_target.xcode_target}: #{change_reason}"
|
115
131
|
end.compact
|
@@ -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 =
|
169
|
+
targets_by_product_name = targets.each_with_object({}) do |t, h|
|
154
170
|
next unless t.respond_to?(:product_reference)
|
155
|
-
[File.basename(t.product_reference.path)
|
156
|
-
|
171
|
+
h[File.basename(t.product_reference.path)] = t
|
172
|
+
h[File.basename(t.product_reference.name)] = t if t.product_reference.name
|
173
|
+
end
|
157
174
|
|
158
175
|
find_dep = ->(td) { targets_by_uuid[td.native_target_uuid] || targets_by_name[td.name] }
|
159
176
|
target_deps = lambda do |target|
|
@@ -166,7 +183,7 @@ module Refinement
|
|
166
183
|
# yay auto-linking
|
167
184
|
if (phase = target.frameworks_build_phases)
|
168
185
|
phase.files_references.each do |fr|
|
169
|
-
if (dt = fr
|
186
|
+
if (dt = fr&.path && targets_by_product_name[File.basename(fr.path)])
|
170
187
|
target_dependencies << dt
|
171
188
|
end
|
172
189
|
end
|
@@ -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
|
55
|
+
raise ArgumentError, "level must be positive, not #{distance_from_target}" if distance_from_target.negative?
|
56
|
+
|
53
57
|
change_reason = direct_change_reason
|
54
|
-
if distance_from_target
|
58
|
+
if distance_from_target.positive?
|
55
59
|
change_reason ||= Refinement.map_find(dependencies) do |dependency|
|
56
|
-
|
60
|
+
unless (dependency_change_reason = dependency.change_reason(level: [:at_most_n_away, level.last.pred]))
|
61
|
+
next
|
62
|
+
end
|
63
|
+
|
57
64
|
"dependency #{dependency} changed because #{dependency_change_reason}"
|
58
65
|
end
|
59
66
|
end
|
data/lib/refinement/changeset.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'cocoapods/executable'
|
2
4
|
require 'set'
|
3
5
|
|
@@ -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
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
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
|
-
|
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
|
-
|
75
|
+
@cached_yaml ||= {}
|
76
|
+
|
77
|
+
dig_yaml = lambda do |yaml, path|
|
72
78
|
return yaml if DOES_NOT_EXIST == yaml
|
73
|
-
|
79
|
+
|
80
|
+
object = @cached_yaml[path] ||= YAML.safe_load(yaml, [Symbol])
|
74
81
|
if keypath.empty?
|
75
82
|
object
|
76
83
|
elsif object.respond_to?(:dig)
|
@@ -82,8 +89,8 @@ module Refinement
|
|
82
89
|
end
|
83
90
|
end
|
84
91
|
|
85
|
-
prior = dig_yaml[prior_contents]
|
86
|
-
current = dig_yaml[contents]
|
92
|
+
prior = dig_yaml[prior_contents, :prior]
|
93
|
+
current = dig_yaml[contents, :current]
|
87
94
|
|
88
95
|
require 'xcodeproj/differ'
|
89
96
|
|
data/lib/refinement/cli.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'claide'
|
2
4
|
|
3
5
|
module Refinement
|
@@ -48,6 +50,7 @@ module Refinement
|
|
48
50
|
puts analyzer.format_changes if @print_changes
|
49
51
|
|
50
52
|
return unless @scheme
|
53
|
+
|
51
54
|
analyzer.filtered_scheme(scheme_path: @scheme, log_changes: @print_scheme_changes, filter_scheme_for_build_action: @filter_scheme_for_build_action)
|
52
55
|
.save_as(@scheme.gsub(%r{\.(xcodeproj|xcworkspace)/.+}, '.\1'), File.basename(@scheme, '.xcscheme'), true)
|
53
56
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Refinement
|
2
4
|
# Called after CocoaPods installation to write an augmenting file that
|
3
5
|
# takes into account changes to Pod configuration,
|
@@ -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|
|
data/lib/refinement/used_path.rb
CHANGED
data/lib/refinement/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: refinement
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.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:
|
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.
|
81
|
+
version: '2.3'
|
82
82
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
83
|
requirements:
|
84
84
|
- - ">="
|
85
85
|
- !ruby/object:Gem::Version
|
86
86
|
version: '0'
|
87
87
|
requirements: []
|
88
|
-
rubygems_version: 3.0.
|
89
|
-
signing_key:
|
88
|
+
rubygems_version: 3.0.1
|
89
|
+
signing_key:
|
90
90
|
specification_version: 4
|
91
91
|
summary: Generates a list of Xcode targets to build & test as a result of a git diff.
|
92
92
|
test_files: []
|