selective-ruby-core 0.1.5-x86_64-linux → 0.1.7-x86_64-linux
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 +4 -4
- data/lib/bin/build_env.sh +14 -7
- data/lib/bin/file_correlation_collector.sh +141 -0
- data/lib/selective/ruby/core/controller.rb +79 -126
- data/lib/selective/ruby/core/file_correlator.rb +38 -0
- data/lib/selective/ruby/core/helper.rb +76 -0
- data/lib/selective/ruby/core/version.rb +1 -1
- data/lib/selective-ruby-core.rb +5 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3ee42cddb33203a7cf1604187c67426b3b7f5af1fa0e6fbb0d1516724c3e6c8e
|
4
|
+
data.tar.gz: d58c25995521db66960c51a860a778303bdb5eb5be5d11b81cf00c066d552b26
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 85d543fc9a3fd71ce55d8968eb4093c327a900841717f23307edc03a0476b7f10df81622edc4133b0d6fd9c7164689cc031d2a68faefc9700819a64026917ccc
|
7
|
+
data.tar.gz: 36648d460cfb7c7e3604d066645571e936ed3a00b2dcfb44326aa399758190a446313e18575f06e3b9ebcb4fb6748982c9dc066f1d8b22863d1fb253a79783a8
|
data/lib/bin/build_env.sh
CHANGED
@@ -2,26 +2,32 @@
|
|
2
2
|
|
3
3
|
# Detect the platform (only GitHub Actions in this case)
|
4
4
|
if [ -n "$GITHUB_ACTIONS" ]; then
|
5
|
-
# Get environment variables
|
6
5
|
platform=github_actions
|
7
6
|
branch=${SELECTIVE_BRANCH:-${GITHUB_HEAD_REF:-$GITHUB_REF_NAME}}
|
8
|
-
pr_title=$SELECTIVE_PR_TITLE
|
9
7
|
target_branch=${SELECTIVE_TARGET_BRANCH:-$GITHUB_BASE_REF}
|
10
8
|
actor=$GITHUB_ACTOR
|
11
9
|
sha=${SELECTIVE_SHA:-$GITHUB_SHA}
|
12
10
|
run_id=${SELECTIVE_RUN_ID:-$GITHUB_RUN_ID}
|
13
11
|
run_attempt=${SELECTIVE_RUN_ATTEMPT:-$GITHUB_RUN_ATTEMPT}
|
14
|
-
|
12
|
+
runner_id=$SELECTIVE_RUNNER_ID
|
13
|
+
elif [ -n "$CIRCLECI" ]; then
|
14
|
+
platform=circleci
|
15
|
+
branch=${SELECTIVE_BRANCH:-$CIRCLE_BRANCH}
|
16
|
+
target_branch=$SELECTIVE_TARGET_BRANCH
|
17
|
+
actor=${SELECTIVE_ACTOR:-${CIRCLE_USERNAME:-$CIRCLE_PR_USERNAME}}
|
18
|
+
sha=${SELECTIVE_SHA:-$CIRCLE_SHA1}
|
19
|
+
run_id=$SELECTIVE_RUN_ID
|
20
|
+
run_attempt=${SELECTIVE_RUN_ATTEMPT:-$CIRCLE_BUILD_NUM}
|
21
|
+
runner_id=${SELECTIVE_RUNNER_ID:-$CIRCLE_NODE_INDEX}
|
15
22
|
else
|
16
23
|
platform=$SELECTIVE_PLATFORM
|
17
24
|
branch=$SELECTIVE_BRANCH
|
18
|
-
pr_title=$SELECTIVE_PR_TITLE
|
19
25
|
target_branch=$SELECTIVE_TARGET_BRANCH
|
20
26
|
actor=$SELECTIVE_ACTOR
|
21
27
|
sha=$SELECTIVE_SHA
|
22
28
|
run_id=$SELECTIVE_RUN_ID
|
23
29
|
run_attempt=$SELECTIVE_RUN_ATTEMPT
|
24
|
-
|
30
|
+
runner_id=$SELECTIVE_RUNNER_ID
|
25
31
|
fi
|
26
32
|
|
27
33
|
# Output the JSON
|
@@ -29,12 +35,13 @@ cat <<EOF
|
|
29
35
|
{
|
30
36
|
"platform": "$platform",
|
31
37
|
"branch": "$branch",
|
32
|
-
"pr_title": "$
|
38
|
+
"pr_title": "$SELECTIVE_PR_TITLE",
|
33
39
|
"target_branch": "$target_branch",
|
34
40
|
"actor": "$actor",
|
35
41
|
"sha": "$sha",
|
36
42
|
"run_id": "$run_id",
|
37
43
|
"run_attempt": "$run_attempt",
|
38
|
-
"commit_message": "$
|
44
|
+
"commit_message": "$(git log --format=%s -n 1 $sha)",
|
45
|
+
"runner_id": "$runner_id"
|
39
46
|
}
|
40
47
|
EOF
|
@@ -0,0 +1,141 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
# The first argument is the number of commits to process
|
4
|
+
branch=$1
|
5
|
+
num_commits=$2
|
6
|
+
|
7
|
+
# Initialize an associative array to hold the files to check
|
8
|
+
declare -A files_to_check
|
9
|
+
|
10
|
+
# Populate the array with the script arguments, starting from the second argument
|
11
|
+
for file in "${@:3}"
|
12
|
+
do
|
13
|
+
files_to_check["$file"]=1
|
14
|
+
done
|
15
|
+
|
16
|
+
# Get a list of all commit hashes, in reverse order
|
17
|
+
all_commits=$(git log origin/$branch --no-merges --format=%H --reverse -n $num_commits)
|
18
|
+
|
19
|
+
# Initialize an associative array to store the test files
|
20
|
+
declare -A test_files
|
21
|
+
declare -A uncorrelated_test_files
|
22
|
+
|
23
|
+
# Initialize an array to store the files changed in the previous commit
|
24
|
+
prev_changed_files=()
|
25
|
+
|
26
|
+
# For each commit...
|
27
|
+
for commit in $all_commits
|
28
|
+
do
|
29
|
+
# Get a list of all files that were changed in the current commit
|
30
|
+
files=$(git diff-tree --no-commit-id --name-only -r $commit)
|
31
|
+
|
32
|
+
declare -A correlated_test_files
|
33
|
+
|
34
|
+
# # For each file in the list of files changed in the previous commit...
|
35
|
+
for file in "${prev_changed_files[@]}"
|
36
|
+
do
|
37
|
+
# If the file is in the list of files to check...
|
38
|
+
if [[ ${files_to_check[$file]} ]]; then
|
39
|
+
# For each file...
|
40
|
+
for test_file in $files
|
41
|
+
do
|
42
|
+
# If the file is in the test/ directory and ends with _test.rb...
|
43
|
+
if [[ $test_file == spec/*_spec.rb ]]
|
44
|
+
then
|
45
|
+
# Increment the count in the associative array
|
46
|
+
test_files["$file|$test_file"]=$((test_files["$file|$test_file"]+1))
|
47
|
+
# Add the test file to the correlated_test_files array
|
48
|
+
correlated_test_files["$test_file"]=1
|
49
|
+
fi
|
50
|
+
done
|
51
|
+
fi
|
52
|
+
done
|
53
|
+
|
54
|
+
# For each file in the list of files changed in the current commit...
|
55
|
+
for file in $files
|
56
|
+
do
|
57
|
+
# If the file is in the list of files to check...
|
58
|
+
if [[ ${files_to_check[$file]} ]]; then
|
59
|
+
# For each file...
|
60
|
+
for test_file in $files
|
61
|
+
do
|
62
|
+
# If the file is in the test/ directory and ends with _test.rb...
|
63
|
+
if [[ $test_file == spec/*_spec.rb ]]
|
64
|
+
then
|
65
|
+
# Increment the count in the associative array
|
66
|
+
test_files["$file|$test_file"]=$((test_files["$file|$test_file"]+1))
|
67
|
+
# Add the test file to the correlated_test_files array
|
68
|
+
correlated_test_files["$test_file"]=1
|
69
|
+
fi
|
70
|
+
done
|
71
|
+
fi
|
72
|
+
done
|
73
|
+
|
74
|
+
# For each file...
|
75
|
+
for test_file in $files
|
76
|
+
do
|
77
|
+
# If the file is in the test/ directory and ends with _test.rb...
|
78
|
+
if [[ $test_file == spec/*_spec.rb ]]
|
79
|
+
then
|
80
|
+
# If the test file is not correlated to any of the files to check in the current commit...
|
81
|
+
if [[ -z ${correlated_test_files[$test_file]} ]]
|
82
|
+
then
|
83
|
+
# Increment the count in the associative array
|
84
|
+
uncorrelated_test_files["$test_file"]=$((uncorrelated_test_files["$test_file"]+1))
|
85
|
+
fi
|
86
|
+
fi
|
87
|
+
done
|
88
|
+
|
89
|
+
# Clear the correlated_test_files array for the next commit
|
90
|
+
unset correlated_test_files
|
91
|
+
|
92
|
+
# Store the list of files changed in this commit for the next iteration
|
93
|
+
prev_changed_files=($files)
|
94
|
+
done
|
95
|
+
|
96
|
+
# OUTPUT
|
97
|
+
|
98
|
+
# Initialize an associative array to hold the JSON strings for each file
|
99
|
+
declare -A file_jsons
|
100
|
+
|
101
|
+
# Add the test_files to the file_jsons associative array
|
102
|
+
for key in "${!test_files[@]}"
|
103
|
+
do
|
104
|
+
file=${key%|*}
|
105
|
+
test_file=${key#*|}
|
106
|
+
count=${test_files[$key]}
|
107
|
+
# Append to the JSON string for this file
|
108
|
+
file_jsons["$file"]+="\"$test_file\": $count,"
|
109
|
+
done
|
110
|
+
|
111
|
+
# Initialize an empty string for the test_files JSON
|
112
|
+
correlated_files_json=""
|
113
|
+
|
114
|
+
# Add the file_jsons to the correlated_files_json string
|
115
|
+
for file in "${!file_jsons[@]}"
|
116
|
+
do
|
117
|
+
# Remove the trailing comma from the JSON string for this file
|
118
|
+
file_json=${file_jsons[$file]%?}
|
119
|
+
# Append to the correlated_files_json string
|
120
|
+
correlated_files_json+="\"$file\": { $file_json },"
|
121
|
+
done
|
122
|
+
|
123
|
+
# Remove the trailing comma from the correlated_files_json string
|
124
|
+
correlated_files_json=${correlated_files_json%?}
|
125
|
+
|
126
|
+
# Initialize an empty string for the uncorrelated_test_files JSON
|
127
|
+
uncorrelated_files_json=""
|
128
|
+
|
129
|
+
# Add the uncorrelated_test_files to the uncorrelated_files_json string
|
130
|
+
for key in "${!uncorrelated_test_files[@]}"
|
131
|
+
do
|
132
|
+
count=${uncorrelated_test_files[$key]}
|
133
|
+
# Append to the uncorrelated_files_json string
|
134
|
+
uncorrelated_files_json+="\"$key\": $count,"
|
135
|
+
done
|
136
|
+
|
137
|
+
# Remove the trailing comma from the uncorrelated_files_json string
|
138
|
+
uncorrelated_files_json=${uncorrelated_files_json%?}
|
139
|
+
|
140
|
+
# Output the JSON
|
141
|
+
echo "{ \"correlated_files\": { $correlated_files_json }, \"uncorrelated_files\": { $uncorrelated_files_json } }"
|
@@ -1,26 +1,25 @@
|
|
1
1
|
require "logger"
|
2
2
|
require "uri"
|
3
|
-
require "json"
|
4
3
|
require "fileutils"
|
5
|
-
require "open3"
|
6
4
|
|
7
5
|
module Selective
|
8
6
|
module Ruby
|
9
7
|
module Core
|
10
8
|
class Controller
|
9
|
+
include Helper
|
11
10
|
@@selective_suppress_reporting = false
|
12
11
|
|
13
12
|
def initialize(runner, debug: false, log: false)
|
14
13
|
@debug = debug
|
15
14
|
@runner = runner
|
16
15
|
@retries = 0
|
17
|
-
@runner_id = safe_filename(
|
16
|
+
@runner_id = safe_filename(get_runner_id)
|
18
17
|
@logger = init_logger(log)
|
19
18
|
end
|
20
19
|
|
21
20
|
def start(reconnect: false)
|
22
21
|
@pipe = NamedPipe.new("/tmp/#{runner_id}_2", "/tmp/#{runner_id}_1")
|
23
|
-
@transport_pid = spawn_transport_process(reconnect
|
22
|
+
@transport_pid = spawn_transport_process(reconnect: reconnect)
|
24
23
|
|
25
24
|
handle_termination_signals(transport_pid)
|
26
25
|
wait_for_connectivity
|
@@ -51,9 +50,14 @@ module Selective
|
|
51
50
|
|
52
51
|
private
|
53
52
|
|
54
|
-
attr_reader :runner, :pipe, :transport_pid, :retries, :logger, :runner_id
|
53
|
+
attr_reader :runner, :pipe, :transport_pid, :retries, :logger, :runner_id, :diff
|
55
54
|
|
56
|
-
|
55
|
+
def get_runner_id
|
56
|
+
runner_id = build_env.delete("runner_id")
|
57
|
+
return generate_runner_id if runner_id.nil? || runner_id.empty?
|
58
|
+
|
59
|
+
runner_id
|
60
|
+
end
|
57
61
|
|
58
62
|
def init_logger(enabled)
|
59
63
|
if enabled
|
@@ -70,9 +74,7 @@ module Selective
|
|
70
74
|
response = JSON.parse(message, symbolize_names: true)
|
71
75
|
|
72
76
|
@logger.info("Received Command: #{response}")
|
73
|
-
|
74
|
-
|
75
|
-
break
|
77
|
+
break if handle_command(response) == :break
|
76
78
|
end
|
77
79
|
end
|
78
80
|
|
@@ -97,7 +99,7 @@ module Selective
|
|
97
99
|
"selgen-#{SecureRandom.hex(4)}"
|
98
100
|
end
|
99
101
|
|
100
|
-
def transport_url
|
102
|
+
def transport_url(reconnect: false)
|
101
103
|
@transport_url ||= begin
|
102
104
|
api_key = ENV.fetch("SELECTIVE_API_KEY")
|
103
105
|
host = ENV.fetch("SELECTIVE_HOST", "wss://app.selective.ci")
|
@@ -113,9 +115,16 @@ module Selective
|
|
113
115
|
"run_id" => run_id,
|
114
116
|
"run_attempt" => run_attempt,
|
115
117
|
"api_key" => api_key,
|
116
|
-
"runner_id" => runner_id
|
118
|
+
"runner_id" => runner_id,
|
119
|
+
"language" => "ruby",
|
120
|
+
"core_version" => Selective::Ruby::Core::VERSION,
|
121
|
+
"framework" => runner.framework,
|
122
|
+
"framework_version" => runner.framework_version,
|
123
|
+
"framework_wrapper_version" => runner.wrapper_version,
|
117
124
|
}.merge(metadata: build_env.to_json)
|
118
125
|
|
126
|
+
prams[:reconnect] = true if reconnect
|
127
|
+
|
119
128
|
query_string = URI.encode_www_form(params)
|
120
129
|
|
121
130
|
"#{host}/transport/websocket?#{query_string}"
|
@@ -124,15 +133,14 @@ module Selective
|
|
124
133
|
|
125
134
|
def build_env
|
126
135
|
@build_env ||= begin
|
127
|
-
result = `#{
|
136
|
+
result = `#{File.join(ROOT_GEM_PATH, "lib", "bin", "build_env.sh")}`
|
128
137
|
JSON.parse(result)
|
129
138
|
end
|
130
139
|
end
|
131
140
|
|
132
|
-
def spawn_transport_process(
|
133
|
-
|
134
|
-
|
135
|
-
get_transport_path = File.join(root_path, "bin", "get_transport")
|
141
|
+
def spawn_transport_process(reconnect: false)
|
142
|
+
transport_path = File.join(ROOT_GEM_PATH, "lib", "bin", "transport")
|
143
|
+
get_transport_path = File.join(ROOT_GEM_PATH, "bin", "get_transport")
|
136
144
|
|
137
145
|
# The get_transport script is not released with the gem, so this
|
138
146
|
# code is intended for development/CI purposes.
|
@@ -147,7 +155,7 @@ module Selective
|
|
147
155
|
end
|
148
156
|
end
|
149
157
|
|
150
|
-
Process.spawn(transport_path,
|
158
|
+
Process.spawn(transport_path, transport_url(reconnect: reconnect), runner_id).tap do |pid|
|
151
159
|
Process.detach(pid)
|
152
160
|
end
|
153
161
|
end
|
@@ -211,154 +219,99 @@ module Selective
|
|
211
219
|
# Process already gone noop
|
212
220
|
end
|
213
221
|
|
214
|
-
def handle_command(
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
handle_test_manifest
|
220
|
-
when "run_test_cases"
|
221
|
-
handle_run_test_cases(response[:test_case_ids])
|
222
|
-
when "remove_failed_test_case_result"
|
223
|
-
handle_remove_failed_test_case_result(response[:test_case_id])
|
224
|
-
when "reconnect"
|
225
|
-
handle_reconnect
|
226
|
-
when "print_message"
|
227
|
-
handle_print_message(response[:message])
|
228
|
-
when "close"
|
229
|
-
handle_close(response[:exit_status])
|
230
|
-
# This return is here for the sake of test where
|
231
|
-
# we cannot exit but we need to break the loop
|
232
|
-
return false
|
233
|
-
else
|
234
|
-
raise "Unknown command received: #{response[:command]}" if debug?
|
235
|
-
end
|
222
|
+
def handle_command(data)
|
223
|
+
send("handle_#{data[:command]}", data)
|
224
|
+
rescue NoMethodError
|
225
|
+
raise "Unknown command received: #{data[:command]}" if debug?
|
226
|
+
end
|
236
227
|
|
237
|
-
|
228
|
+
def handle_print_notice(data)
|
229
|
+
print_notice(data[:message])
|
238
230
|
end
|
239
231
|
|
240
|
-
def handle_reconnect
|
232
|
+
def handle_reconnect(_data)
|
241
233
|
kill_transport
|
242
234
|
pipe.reset!
|
243
235
|
start(reconnect: true)
|
244
236
|
end
|
245
237
|
|
246
|
-
def handle_test_manifest
|
238
|
+
def handle_test_manifest(_data)
|
247
239
|
self.class.restore_reporting!
|
248
240
|
@logger.info("Sending Response: test_manifest")
|
249
241
|
data = {test_cases: runner.manifest["examples"]}
|
250
|
-
|
242
|
+
num_commits = build_env["num_commits"] || 1000
|
243
|
+
if (diff = get_diff(num_commits))
|
244
|
+
data[:modified_test_files] = modified_test_files(diff)
|
245
|
+
data[:correlated_files] = correlated_files(diff, num_commits)
|
246
|
+
end
|
251
247
|
write({type: "test_manifest", data: data})
|
252
248
|
end
|
253
249
|
|
254
|
-
def handle_run_test_cases(
|
255
|
-
runner.run_test_cases(
|
250
|
+
def handle_run_test_cases(data)
|
251
|
+
runner.run_test_cases(data[:test_case_ids], method(:test_case_callback))
|
256
252
|
end
|
257
253
|
|
258
|
-
def
|
259
|
-
|
260
|
-
write({type: "test_case_result", data: test_case})
|
261
|
-
end
|
262
|
-
|
263
|
-
def handle_remove_failed_test_case_result(test_case_id)
|
264
|
-
runner.remove_failed_test_case_result(test_case_id)
|
254
|
+
def handle_remove_failed_test_case_result(data)
|
255
|
+
runner.remove_failed_test_case_result(data[:test_case_id])
|
265
256
|
end
|
266
257
|
|
267
|
-
def
|
268
|
-
|
269
|
-
target_branch = build_env["target_branch"]
|
270
|
-
return [] if target_branch.nil? || target_branch.empty?
|
271
|
-
|
272
|
-
output, status = Open3.capture2e("git diff #{target_branch} --name-only")
|
273
|
-
|
274
|
-
if status.success?
|
275
|
-
output.split("\n").filter do |f|
|
276
|
-
f.match?(/^#{runner.base_test_path}/)
|
277
|
-
end
|
278
|
-
end
|
279
|
-
end
|
258
|
+
def handle_print_message(data)
|
259
|
+
print_warning(data[:message])
|
280
260
|
end
|
281
261
|
|
282
|
-
def
|
283
|
-
|
284
|
-
end
|
285
|
-
|
286
|
-
def handle_close(exit_status = nil)
|
262
|
+
def handle_close(data)
|
263
|
+
exit_status = data[:exit_status]
|
287
264
|
self.class.restore_reporting!
|
288
265
|
runner.finish unless exit_status.is_a?(Integer)
|
289
266
|
|
290
267
|
kill_transport
|
291
268
|
pipe.delete_pipes
|
292
269
|
exit(exit_status || runner.exit_status)
|
270
|
+
# This :break is here for the sake of test where
|
271
|
+
# we cannot exit but we need to break the loop
|
272
|
+
:break
|
293
273
|
end
|
294
274
|
|
295
|
-
def
|
296
|
-
|
275
|
+
def correlated_files(diff, num_commits)
|
276
|
+
Selective::Ruby::Core::FileCorrelator.new(diff, num_commits, build_env["target_branch"]).correlate
|
297
277
|
end
|
298
278
|
|
299
|
-
def
|
300
|
-
|
301
|
-
|
302
|
-
.gsub(/^\.+|\.+$/, '')
|
303
|
-
.strip[0, 255]
|
279
|
+
def test_case_callback(test_case)
|
280
|
+
@logger.info("Sending Response: test_case_result: #{test_case[:id]}")
|
281
|
+
write({type: "test_case_result", data: test_case})
|
304
282
|
end
|
305
283
|
|
306
|
-
def
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
An error occurred. Please rerun with --debug
|
312
|
-
and contact support at https://selective.ci/support
|
313
|
-
TEXT
|
314
|
-
|
315
|
-
unless @banner_displayed
|
316
|
-
header = <<~TEXT
|
317
|
-
#{banner}
|
318
|
-
|
319
|
-
#{header}
|
320
|
-
TEXT
|
284
|
+
def modified_test_files(diff)
|
285
|
+
@modified_test_files ||= begin
|
286
|
+
diff.filter do |f|
|
287
|
+
f.match?(/^#{runner.base_test_path}/)
|
288
|
+
end
|
321
289
|
end
|
322
|
-
|
323
|
-
puts_indented <<~TEXT
|
324
|
-
\e[31m
|
325
|
-
#{header if include_header}
|
326
|
-
#{e.message}
|
327
|
-
\e[0m
|
328
|
-
TEXT
|
329
|
-
|
330
|
-
exit 1
|
331
290
|
end
|
332
291
|
|
333
|
-
def
|
334
|
-
|
335
|
-
|
336
|
-
#{message}
|
337
|
-
\e[0m
|
338
|
-
TEXT
|
339
|
-
end
|
292
|
+
def get_diff(num_commits)
|
293
|
+
target_branch = build_env["target_branch"]
|
294
|
+
return [] if target_branch.nil? || target_branch.empty?
|
340
295
|
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
296
|
+
Open3.capture2e("git fetch origin #{target_branch} --depth=#{num_commits}").then do |output, status|
|
297
|
+
unless status.success?
|
298
|
+
print_warning "Selective was unable to fetch the target branch. This may result in a sub-optimal test order. If the issue persists, please contact support. The output was:\n\n#{output}"
|
299
|
+
return []
|
300
|
+
end
|
301
|
+
end
|
347
302
|
|
348
|
-
|
349
|
-
|
303
|
+
Open3.capture2e("git diff origin/#{target_branch} --name-only").then do |output, status|
|
304
|
+
if status.success?
|
305
|
+
output.split("\n")
|
306
|
+
else
|
307
|
+
print_warning "Selective was unable to diff with the target branch. This may result in a sub-optimal test order. If the issue persists, please contact support. The output was:\n\n#{output}"
|
308
|
+
[]
|
309
|
+
end
|
310
|
+
end
|
350
311
|
end
|
351
312
|
|
352
|
-
def
|
353
|
-
@
|
354
|
-
<<~BANNER
|
355
|
-
____ _ _ _
|
356
|
-
/ ___| ___| | ___ ___| |_(_)_ _____
|
357
|
-
\\___ \\ / _ \\ |/ _ \\/ __| __| \\ \\ / / _ \\
|
358
|
-
___) | __/ | __/ (__| |_| |\\ V / __/
|
359
|
-
|____/ \\___|_|\\___|\\___|\\__|_| \\_/ \\___|
|
360
|
-
________________________________________
|
361
|
-
BANNER
|
313
|
+
def debug?
|
314
|
+
@debug
|
362
315
|
end
|
363
316
|
end
|
364
317
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Selective
|
2
|
+
module Ruby
|
3
|
+
module Core
|
4
|
+
class FileCorrelator
|
5
|
+
include Helper
|
6
|
+
|
7
|
+
class FileCorrelatorError < StandardError; end
|
8
|
+
|
9
|
+
FILE_CORRELATION_COLLECTOR_PATH = File.join(ROOT_GEM_PATH, "lib", "bin", "file_correlation_collector.sh")
|
10
|
+
|
11
|
+
def initialize(diff, num_commits, target_branch)
|
12
|
+
@diff = diff
|
13
|
+
@num_commits = num_commits
|
14
|
+
@target_branch = target_branch
|
15
|
+
end
|
16
|
+
|
17
|
+
def correlate
|
18
|
+
JSON.parse(get_correlated_files, symbolize_names: true)
|
19
|
+
rescue FileCorrelatorError, JSON::ParserError
|
20
|
+
print_warning "Selective was unable to correlate the diff to test files. This may result in a sub-optimal test order. If the issue persists, please contact support."
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
attr_reader :diff, :num_commits, :target_branch
|
26
|
+
|
27
|
+
def get_correlated_files
|
28
|
+
Open3.capture2e("#{FILE_CORRELATION_COLLECTOR_PATH} #{target_branch} #{num_commits} #{diff.join(" ")}").then do |output, status|
|
29
|
+
|
30
|
+
raise FileCorrelatorError unless status.success?
|
31
|
+
|
32
|
+
output
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Selective
|
2
|
+
module Ruby
|
3
|
+
module Core
|
4
|
+
module Helper
|
5
|
+
def safe_filename(filename)
|
6
|
+
filename
|
7
|
+
.gsub(/[\/\\:*?"<>|\n\r]+/, '_')
|
8
|
+
.gsub(/^\.+|\.+$/, '')
|
9
|
+
.strip[0, 255]
|
10
|
+
end
|
11
|
+
|
12
|
+
def with_error_handling(include_header: true)
|
13
|
+
yield
|
14
|
+
rescue => e
|
15
|
+
raise e if debug?
|
16
|
+
header = <<~TEXT
|
17
|
+
An error occurred. Please rerun with --debug
|
18
|
+
and contact support at https://selective.ci/support
|
19
|
+
TEXT
|
20
|
+
|
21
|
+
unless $selective_banner_displayed
|
22
|
+
header = <<~TEXT
|
23
|
+
#{banner}
|
24
|
+
|
25
|
+
#{header}
|
26
|
+
TEXT
|
27
|
+
end
|
28
|
+
|
29
|
+
puts_indented <<~TEXT
|
30
|
+
\e[31m
|
31
|
+
#{header if include_header}
|
32
|
+
#{e.message}
|
33
|
+
\e[0m
|
34
|
+
TEXT
|
35
|
+
|
36
|
+
exit 1
|
37
|
+
end
|
38
|
+
|
39
|
+
def print_warning(message)
|
40
|
+
puts_indented <<~TEXT
|
41
|
+
\e[33m
|
42
|
+
#{message}
|
43
|
+
\e[0m
|
44
|
+
TEXT
|
45
|
+
end
|
46
|
+
|
47
|
+
def print_notice(message)
|
48
|
+
puts_indented <<~TEXT
|
49
|
+
#{banner unless $selective_banner_displayed}
|
50
|
+
#{message}
|
51
|
+
TEXT
|
52
|
+
end
|
53
|
+
|
54
|
+
def puts_indented(text)
|
55
|
+
puts text.gsub(/^/, " ")
|
56
|
+
end
|
57
|
+
|
58
|
+
def banner
|
59
|
+
Helper.banner
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.banner
|
63
|
+
$selective_banner_displayed = true
|
64
|
+
<<~BANNER
|
65
|
+
____ _ _ _
|
66
|
+
/ ___| ___| | ___ ___| |_(_)_ _____
|
67
|
+
\\___ \\ / _ \\ |/ _ \\/ __| __| \\ \\ / / _ \\
|
68
|
+
___) | __/ | __/ (__| |_| |\\ V / __/
|
69
|
+
|____/ \\___|_|\\___|\\___|\\__|_| \\_/ \\___|
|
70
|
+
________________________________________
|
71
|
+
BANNER
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/lib/selective-ruby-core.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "zeitwerk"
|
4
|
+
require "json"
|
5
|
+
require "open3"
|
6
|
+
require "#{__dir__}/selective/ruby/core/version"
|
4
7
|
|
5
8
|
loader = Zeitwerk::Loader.for_gem(warn_on_extra_files: false)
|
6
9
|
loader.ignore("#{__dir__}/selective-ruby-core.rb")
|
@@ -12,6 +15,8 @@ module Selective
|
|
12
15
|
module Core
|
13
16
|
class Error < StandardError; end
|
14
17
|
|
18
|
+
ROOT_GEM_PATH = Gem.loaded_specs["selective-ruby-core"].full_gem_path
|
19
|
+
|
15
20
|
@@available_runners = {}
|
16
21
|
|
17
22
|
def self.register_runner(name, runner_class)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: selective-ruby-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.7
|
5
5
|
platform: x86_64-linux
|
6
6
|
authors:
|
7
7
|
- Benjamin Wood
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2024-01-05 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: zeitwerk
|
@@ -41,9 +41,12 @@ files:
|
|
41
41
|
- Rakefile
|
42
42
|
- exe/selective
|
43
43
|
- lib/bin/build_env.sh
|
44
|
+
- lib/bin/file_correlation_collector.sh
|
44
45
|
- lib/bin/transport
|
45
46
|
- lib/selective-ruby-core.rb
|
46
47
|
- lib/selective/ruby/core/controller.rb
|
48
|
+
- lib/selective/ruby/core/file_correlator.rb
|
49
|
+
- lib/selective/ruby/core/helper.rb
|
47
50
|
- lib/selective/ruby/core/named_pipe.rb
|
48
51
|
- lib/selective/ruby/core/version.rb
|
49
52
|
homepage: https://www.selective.ci
|