selective-ruby-core 0.1.0-aarch64-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 +7 -0
- data/LICENSE +21 -0
- data/Rakefile +18 -0
- data/exe/selective +16 -0
- data/lib/bin/build_env.sh +34 -0
- data/lib/bin/transport +0 -0
- data/lib/selective/ruby/core/controller.rb +313 -0
- data/lib/selective/ruby/core/named_pipe.rb +103 -0
- data/lib/selective/ruby/core/version.rb +9 -0
- data/lib/selective-ruby-core.rb +64 -0
- metadata +75 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 0a0e4dc18c3cb6d8d0b4821b8ff26bcb0082a3a501593493207d99dd9814305c
|
4
|
+
data.tar.gz: 2791a6864d2be9e577d0e5a00600df3f5da2d28f9a3207e64ed1bfddfa7129a1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d8ae66cad83d5e643f5ae882b30424bd06a8c711dfd22b76d805f7f3c610c734901ae7d3adf68c696f07c9a0451b14fbdee3f760945a9775a160a1e5ad199be3
|
7
|
+
data.tar.gz: 1d2e7be21539b9fcdccfa23e093892723aa7fedccc8c6d89128fc9d9eff5a34522cd2f1c3e3d4c94ab569fe675f85a6c670488dac11660b4c8a72fd4b088f2f1
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2023 Selective
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
|
5
|
+
# Override the guard_clean task to be a no-op
|
6
|
+
Rake::Task["release:guard_clean"].clear
|
7
|
+
|
8
|
+
task "release:guard_clean" do
|
9
|
+
# Intentionally blank to skip the check
|
10
|
+
end
|
11
|
+
|
12
|
+
require "rspec/core/rake_task"
|
13
|
+
|
14
|
+
RSpec::Core::RakeTask.new(:spec)
|
15
|
+
|
16
|
+
require "standard/rake"
|
17
|
+
|
18
|
+
task default: %i[spec standard]
|
data/exe/selective
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
|
5
|
+
# We test selective-ruby using selective-ruby. This means that
|
6
|
+
# SimpleCov.start must be called before our code is loaded.
|
7
|
+
if ENV["SELECTIVE_SIMPLECOV"]
|
8
|
+
require "simplecov"
|
9
|
+
SimpleCov.start do
|
10
|
+
add_filter "/spec/"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
require "selective-ruby-core"
|
15
|
+
|
16
|
+
Selective::Ruby::Core::Init.run(ARGV.dup)
|
@@ -0,0 +1,34 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
# Detect the platform (only GitHub Actions in this case)
|
4
|
+
if [ -n "$GITHUB_ACTIONS" ]; then
|
5
|
+
# Get environment variables
|
6
|
+
platform="github_actions"
|
7
|
+
branch="${GITHUB_HEAD_REF:-$GITHUB_REF_NAME}"
|
8
|
+
pr_title="$PR_TITLE"
|
9
|
+
target_branch="${GITHUB_BASE_REF}"
|
10
|
+
actor="$GITHUB_ACTOR"
|
11
|
+
sha="$GITHUB_SHA"
|
12
|
+
commit_message=$(git log --format=%s -n 1 $sha)
|
13
|
+
else
|
14
|
+
platform="$SELECTIVE_PLATFORM"
|
15
|
+
branch="$SELECTIVE_BRANCH"
|
16
|
+
pr_title="$SELECTIVE_PR_TITLE"
|
17
|
+
target_branch="$SELECTIVE_TARGET_BRANCH"
|
18
|
+
actor="$SELECTIVE_ACTOR"
|
19
|
+
sha="$SELECTIVE_SHA"
|
20
|
+
commit_message=$(git log --format=%s -n 1 $sha)
|
21
|
+
fi
|
22
|
+
|
23
|
+
# Output the JSON
|
24
|
+
cat <<EOF
|
25
|
+
{
|
26
|
+
"platform": "$platform",
|
27
|
+
"branch": "$branch",
|
28
|
+
"pr_title": "$pr_title",
|
29
|
+
"target_branch": "$target_branch",
|
30
|
+
"actor": "$actor",
|
31
|
+
"sha": "$sha",
|
32
|
+
"commit_message": "$commit_message"
|
33
|
+
}
|
34
|
+
EOF
|
data/lib/bin/transport
ADDED
Binary file
|
@@ -0,0 +1,313 @@
|
|
1
|
+
require "logger"
|
2
|
+
require "uri"
|
3
|
+
require "json"
|
4
|
+
|
5
|
+
module Selective
|
6
|
+
module Ruby
|
7
|
+
module Core
|
8
|
+
class Controller
|
9
|
+
@@selective_suppress_reporting = false
|
10
|
+
|
11
|
+
def initialize(runner, debug = false)
|
12
|
+
@debug = debug
|
13
|
+
@runner = runner
|
14
|
+
@retries = 0
|
15
|
+
@runner_id = ENV.fetch("SELECTIVE_RUNNER_ID", generate_runner_id)
|
16
|
+
@logger = Logger.new("log/#{runner_id}.log")
|
17
|
+
end
|
18
|
+
|
19
|
+
def start(reconnect: false)
|
20
|
+
@pipe = NamedPipe.new("/tmp/#{runner_id}_2", "/tmp/#{runner_id}_1")
|
21
|
+
@transport_pid = spawn_transport_process(reconnect ? transport_url + "&reconnect=true" : transport_url)
|
22
|
+
|
23
|
+
handle_termination_signals(transport_pid)
|
24
|
+
run_main_loop
|
25
|
+
rescue NamedPipe::PipeClosedError
|
26
|
+
retry!
|
27
|
+
rescue => e
|
28
|
+
with_error_handling { raise e }
|
29
|
+
end
|
30
|
+
|
31
|
+
def exec
|
32
|
+
runner.exec
|
33
|
+
rescue => e
|
34
|
+
with_error_handling(include_header: false) { raise e }
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.suppress_reporting!
|
38
|
+
@@selective_suppress_reporting = true
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.restore_reporting!
|
42
|
+
@@selective_suppress_reporting = false
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.suppress_reporting?
|
46
|
+
@@selective_suppress_reporting
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
attr_reader :runner, :pipe, :transport_pid, :retries, :logger, :runner_id
|
52
|
+
|
53
|
+
BUILD_ENV_SCRIPT_PATH = "../../../bin/build_env.sh".freeze
|
54
|
+
|
55
|
+
def run_main_loop
|
56
|
+
loop do
|
57
|
+
message = pipe.read
|
58
|
+
next sleep(0.1) if message.nil? || message.empty?
|
59
|
+
|
60
|
+
response = JSON.parse(message, symbolize_names: true)
|
61
|
+
|
62
|
+
@logger.info("Received Command: #{response}")
|
63
|
+
next if handle_command(response)
|
64
|
+
|
65
|
+
break
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def retry!
|
70
|
+
@retries += 1
|
71
|
+
|
72
|
+
with_error_handling { raise "Too many retries" } if retries > 4
|
73
|
+
|
74
|
+
puts("Retrying in #{retries} seconds...")
|
75
|
+
sleep(retries)
|
76
|
+
kill_transport
|
77
|
+
|
78
|
+
pipe.reset!
|
79
|
+
start(reconnect: true)
|
80
|
+
end
|
81
|
+
|
82
|
+
def write(data)
|
83
|
+
pipe.write JSON.dump(data)
|
84
|
+
end
|
85
|
+
|
86
|
+
def generate_runner_id
|
87
|
+
"selgen-#{SecureRandom.hex(4)}"
|
88
|
+
end
|
89
|
+
|
90
|
+
def transport_url
|
91
|
+
@transport_url ||= begin
|
92
|
+
api_key = ENV.fetch("SELECTIVE_API_KEY")
|
93
|
+
run_id = ENV.fetch("SELECTIVE_RUN_ID")
|
94
|
+
run_attempt = ENV.fetch("SELECTIVE_RUN_ATTEMPT", SecureRandom.uuid)
|
95
|
+
host = ENV.fetch("SELECTIVE_HOST", "wss://app.selective.ci")
|
96
|
+
|
97
|
+
# Validate that host is a valid websocket url(starts with ws:// or wss://)
|
98
|
+
raise "Invalid host: #{host}" unless host.match?(/^wss?:\/\//)
|
99
|
+
|
100
|
+
params = {
|
101
|
+
"run_id" => run_id,
|
102
|
+
"run_attempt" => run_attempt,
|
103
|
+
"api_key" => api_key,
|
104
|
+
"runner_id" => runner_id
|
105
|
+
}.merge(metadata: build_env.to_json)
|
106
|
+
|
107
|
+
query_string = URI.encode_www_form(params)
|
108
|
+
|
109
|
+
"#{host}/transport/websocket?#{query_string}"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def build_env
|
114
|
+
result = `#{Pathname.new(__dir__) + BUILD_ENV_SCRIPT_PATH}`
|
115
|
+
JSON.parse(result)
|
116
|
+
end
|
117
|
+
|
118
|
+
def spawn_transport_process(url)
|
119
|
+
root_path = Gem.loaded_specs["selective-ruby-core"].full_gem_path
|
120
|
+
transport_path = File.join(root_path, "lib", "bin", "transport")
|
121
|
+
get_transport_path = File.join(root_path, "bin", "get_transport")
|
122
|
+
|
123
|
+
# The get_transport script is not released with the gem, so this
|
124
|
+
# code is intended for development/CI purposes.
|
125
|
+
if !File.exist?(transport_path) && File.exist?(get_transport_path)
|
126
|
+
require "open3"
|
127
|
+
output, status = Open3.capture2e(get_transport_path)
|
128
|
+
if !status.success?
|
129
|
+
puts <<~TEXT
|
130
|
+
Failed to download transport binary.
|
131
|
+
|
132
|
+
#{output}
|
133
|
+
TEXT
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
Process.spawn(transport_path, url, runner_id).tap do |pid|
|
138
|
+
Process.detach(pid)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def handle_termination_signals(pid)
|
143
|
+
["INT", "TERM"].each do |signal|
|
144
|
+
Signal.trap(signal) do
|
145
|
+
# :nocov:
|
146
|
+
kill_transport(signal: signal)
|
147
|
+
exit
|
148
|
+
# :nocov:
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def kill_transport(signal: "TERM")
|
154
|
+
begin
|
155
|
+
pipe.write "exit"
|
156
|
+
|
157
|
+
# Give up to 5 seconds for graceful exit
|
158
|
+
# before killing it below
|
159
|
+
1..5.times do
|
160
|
+
Process.getpgid(transport_pid)
|
161
|
+
|
162
|
+
sleep(1)
|
163
|
+
end
|
164
|
+
rescue NamedPipe::PipeClosedError
|
165
|
+
# If the pipe is close, move straight to killing
|
166
|
+
# it forcefully.
|
167
|
+
end
|
168
|
+
|
169
|
+
# :nocov:
|
170
|
+
Process.kill(signal, transport_pid)
|
171
|
+
# :nocov:
|
172
|
+
rescue Errno::ESRCH
|
173
|
+
# Process already gone noop
|
174
|
+
end
|
175
|
+
|
176
|
+
def handle_command(response)
|
177
|
+
case response[:command]
|
178
|
+
when "init"
|
179
|
+
print_init(response[:runner_id])
|
180
|
+
when "test_manifest"
|
181
|
+
handle_test_manifest
|
182
|
+
when "run_test_cases"
|
183
|
+
handle_run_test_cases(response[:test_case_ids])
|
184
|
+
when "remove_failed_test_case_result"
|
185
|
+
handle_remove_failed_test_case_result(response[:test_case_id])
|
186
|
+
when "reconnect"
|
187
|
+
handle_reconnect
|
188
|
+
when "print_message"
|
189
|
+
handle_print_message(response[:message])
|
190
|
+
when "close"
|
191
|
+
handle_close(response[:exit_status])
|
192
|
+
|
193
|
+
# This is here for the sake of test where we
|
194
|
+
# cannot exit but we need to break the loop
|
195
|
+
return false
|
196
|
+
end
|
197
|
+
|
198
|
+
true
|
199
|
+
end
|
200
|
+
|
201
|
+
def handle_reconnect
|
202
|
+
kill_transport
|
203
|
+
pipe.reset!
|
204
|
+
start(reconnect: true)
|
205
|
+
end
|
206
|
+
|
207
|
+
def handle_test_manifest
|
208
|
+
self.class.restore_reporting!
|
209
|
+
@logger.info("Sending Response: test_manifest")
|
210
|
+
write({type: "test_manifest", data: {
|
211
|
+
test_cases: runner.manifest["examples"],
|
212
|
+
modified_test_files: modified_test_files
|
213
|
+
}})
|
214
|
+
end
|
215
|
+
|
216
|
+
def handle_run_test_cases(test_cases)
|
217
|
+
runner.run_test_cases(test_cases, method(:test_case_callback))
|
218
|
+
end
|
219
|
+
|
220
|
+
def test_case_callback(test_case)
|
221
|
+
@logger.info("Sending Response: test_case_result: #{test_case[:id]}")
|
222
|
+
write({type: "test_case_result", data: test_case})
|
223
|
+
end
|
224
|
+
|
225
|
+
def handle_remove_failed_test_case_result(test_case_id)
|
226
|
+
runner.remove_failed_test_case_result(test_case_id)
|
227
|
+
end
|
228
|
+
|
229
|
+
def modified_test_files
|
230
|
+
# Todo: This should find files changed in the current branch
|
231
|
+
`git diff --name-only`.split("\n").filter do |f|
|
232
|
+
f.match?(/^#{runner.base_test_path}/)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
def handle_print_message(message)
|
237
|
+
print_warning(message)
|
238
|
+
end
|
239
|
+
|
240
|
+
def handle_close(exit_status = nil)
|
241
|
+
self.class.restore_reporting!
|
242
|
+
runner.finish unless exit_status.is_a?(Integer)
|
243
|
+
|
244
|
+
kill_transport
|
245
|
+
pipe.delete_pipes
|
246
|
+
exit(exit_status || runner.exit_status)
|
247
|
+
end
|
248
|
+
|
249
|
+
def debug?
|
250
|
+
@debug
|
251
|
+
end
|
252
|
+
|
253
|
+
def with_error_handling(include_header: true)
|
254
|
+
yield
|
255
|
+
rescue => e
|
256
|
+
raise e if debug?
|
257
|
+
header = <<~TEXT
|
258
|
+
An error occurred. Please rerun with --debug
|
259
|
+
and contact support at https://selective.ci/support
|
260
|
+
TEXT
|
261
|
+
|
262
|
+
unless @banner_displayed
|
263
|
+
header = <<~TEXT
|
264
|
+
#{banner}
|
265
|
+
|
266
|
+
#{header}
|
267
|
+
TEXT
|
268
|
+
end
|
269
|
+
|
270
|
+
puts_indented <<~TEXT
|
271
|
+
\e[31m
|
272
|
+
#{header if include_header}
|
273
|
+
#{e.message}
|
274
|
+
\e[0m
|
275
|
+
TEXT
|
276
|
+
|
277
|
+
exit 1
|
278
|
+
end
|
279
|
+
|
280
|
+
def print_warning(message)
|
281
|
+
puts_indented <<~TEXT
|
282
|
+
\e[33m
|
283
|
+
#{message}
|
284
|
+
\e[0m
|
285
|
+
TEXT
|
286
|
+
end
|
287
|
+
|
288
|
+
def print_init(runner_id)
|
289
|
+
puts_indented <<~TEXT
|
290
|
+
#{banner}
|
291
|
+
Runner ID: #{runner_id.gsub("selgen-", "")}
|
292
|
+
TEXT
|
293
|
+
end
|
294
|
+
|
295
|
+
def puts_indented(text)
|
296
|
+
puts text.gsub(/^/, " ")
|
297
|
+
end
|
298
|
+
|
299
|
+
def banner
|
300
|
+
@banner_displayed = true
|
301
|
+
<<~BANNER
|
302
|
+
____ _ _ _
|
303
|
+
/ ___| ___| | ___ ___| |_(_)_ _____
|
304
|
+
\\___ \\ / _ \\ |/ _ \\/ __| __| \\ \\ / / _ \\
|
305
|
+
___) | __/ | __/ (__| |_| |\\ V / __/
|
306
|
+
|____/ \\___|_|\\___|\\___|\\__|_| \\_/ \\___|
|
307
|
+
________________________________________
|
308
|
+
BANNER
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Selective
|
2
|
+
module Ruby
|
3
|
+
module Core
|
4
|
+
class NamedPipe
|
5
|
+
class PipeClosedError < StandardError; end
|
6
|
+
|
7
|
+
attr_reader :read_pipe_path, :write_pipe_path
|
8
|
+
|
9
|
+
def initialize(read_pipe_path, write_pipe_path, skip_reset: false)
|
10
|
+
@read_pipe_path = read_pipe_path
|
11
|
+
@write_pipe_path = write_pipe_path
|
12
|
+
|
13
|
+
delete_pipes unless skip_reset
|
14
|
+
initialize_pipes
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize_pipes
|
18
|
+
create_pipes
|
19
|
+
|
20
|
+
# Open the read and write pipes in separate threads
|
21
|
+
Thread.new do
|
22
|
+
@read_pipe = File.open(read_pipe_path, "r")
|
23
|
+
end
|
24
|
+
Thread.new do
|
25
|
+
@write_pipe = File.open(write_pipe_path, "w")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def write(message)
|
30
|
+
return unless write_pipe
|
31
|
+
|
32
|
+
chunk_size = 1024 # 1KB chunks
|
33
|
+
offset = 0
|
34
|
+
begin
|
35
|
+
while offset < message.bytesize
|
36
|
+
chunk = message.byteslice(offset, chunk_size)
|
37
|
+
|
38
|
+
write_pipe.write(chunk)
|
39
|
+
write_pipe.flush
|
40
|
+
|
41
|
+
offset += chunk_size
|
42
|
+
end
|
43
|
+
|
44
|
+
write_pipe.write("\n")
|
45
|
+
write_pipe.flush
|
46
|
+
rescue Errno::EPIPE
|
47
|
+
raise PipeClosedError
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def read
|
52
|
+
return unless read_pipe
|
53
|
+
begin
|
54
|
+
message = read_pipe.gets.chomp
|
55
|
+
rescue NoMethodError => e
|
56
|
+
if e.name == :chomp
|
57
|
+
raise PipeClosedError
|
58
|
+
else
|
59
|
+
raise e
|
60
|
+
end
|
61
|
+
end
|
62
|
+
message
|
63
|
+
end
|
64
|
+
|
65
|
+
def reset!
|
66
|
+
delete_pipes
|
67
|
+
initialize_pipes
|
68
|
+
end
|
69
|
+
|
70
|
+
def delete_pipes
|
71
|
+
# Close the pipes before deleting them
|
72
|
+
read_pipe&.close
|
73
|
+
write_pipe&.close
|
74
|
+
|
75
|
+
# Allow threads to close before deleting pipes
|
76
|
+
sleep(0.1)
|
77
|
+
|
78
|
+
delete_pipe(read_pipe_path)
|
79
|
+
delete_pipe(write_pipe_path)
|
80
|
+
rescue Errno::EPIPE
|
81
|
+
# Noop
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
attr_reader :read_pipe, :write_pipe
|
87
|
+
|
88
|
+
def create_pipes
|
89
|
+
create_pipe(read_pipe_path)
|
90
|
+
create_pipe(write_pipe_path)
|
91
|
+
end
|
92
|
+
|
93
|
+
def create_pipe(path)
|
94
|
+
system("mkfifo #{path}") unless File.exist?(path)
|
95
|
+
end
|
96
|
+
|
97
|
+
def delete_pipe(path)
|
98
|
+
File.delete(path) if File.exist?(path)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "zeitwerk"
|
4
|
+
|
5
|
+
loader = Zeitwerk::Loader.for_gem(warn_on_extra_files: false)
|
6
|
+
loader.ignore("#{__dir__}/selective-ruby-core.rb")
|
7
|
+
loader.setup
|
8
|
+
|
9
|
+
module Selective
|
10
|
+
module Ruby
|
11
|
+
module Core
|
12
|
+
class Error < StandardError; end
|
13
|
+
|
14
|
+
@@available_runners = {}
|
15
|
+
|
16
|
+
def self.register_runner(name, runner_class)
|
17
|
+
@@available_runners[name] = runner_class
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.runner_for(name)
|
21
|
+
@@available_runners[name] || raise("Unknown runner #{name}")
|
22
|
+
end
|
23
|
+
|
24
|
+
class Init
|
25
|
+
def initialize(args)
|
26
|
+
@debug = !args.delete("--debug").nil?
|
27
|
+
@runner_name, @args, @command = parse_args(args)
|
28
|
+
require_runner
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.run(args)
|
32
|
+
new(args).send(:run)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
attr_reader :debug, :runner_name, :args, :command
|
38
|
+
|
39
|
+
def run
|
40
|
+
Selective::Ruby::Core::Controller.new(runner, debug).send(command)
|
41
|
+
end
|
42
|
+
|
43
|
+
def parse_args(args)
|
44
|
+
# Returns runner_name, args, command
|
45
|
+
if args[0] == "exec" # e.g. selective exec rspec
|
46
|
+
[args[1], args[2..], :exec]
|
47
|
+
else # e.g. selective rspec
|
48
|
+
[args[0], args[1..], :start]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def runner
|
53
|
+
Selective::Ruby::Core.runner_for(runner_name).new(args)
|
54
|
+
end
|
55
|
+
|
56
|
+
def require_runner
|
57
|
+
require "selective-ruby-#{runner_name}"
|
58
|
+
rescue LoadError
|
59
|
+
nil
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
metadata
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: selective-ruby-core
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: aarch64-linux
|
6
|
+
authors:
|
7
|
+
- Benjamin Wood
|
8
|
+
- Nate Vick
|
9
|
+
autorequire:
|
10
|
+
bindir: exe
|
11
|
+
cert_chain: []
|
12
|
+
date: 2023-11-03 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: zeitwerk
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: 2.6.12
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: 2.6.12
|
28
|
+
description: Selective is an intelligent test runner for your current CI provider.
|
29
|
+
Get real-time test results, intelligent ordering based on code changes, shorter
|
30
|
+
run times, automatic flake detection, the ability to re-enqueue failed tests, and
|
31
|
+
more.
|
32
|
+
email:
|
33
|
+
- ben@hint.io
|
34
|
+
- nate@hint.io
|
35
|
+
executables:
|
36
|
+
- selective
|
37
|
+
extensions: []
|
38
|
+
extra_rdoc_files: []
|
39
|
+
files:
|
40
|
+
- LICENSE
|
41
|
+
- Rakefile
|
42
|
+
- exe/selective
|
43
|
+
- lib/bin/build_env.sh
|
44
|
+
- lib/bin/transport
|
45
|
+
- lib/selective-ruby-core.rb
|
46
|
+
- lib/selective/ruby/core/controller.rb
|
47
|
+
- lib/selective/ruby/core/named_pipe.rb
|
48
|
+
- lib/selective/ruby/core/version.rb
|
49
|
+
homepage: https://www.selective.ci
|
50
|
+
licenses:
|
51
|
+
- MIT
|
52
|
+
metadata:
|
53
|
+
homepage_uri: https://www.selective.ci
|
54
|
+
source_code_uri: http://github.com/selectiveci/selective-ruby-core
|
55
|
+
changelog_uri: https://github.com/selectiveci/selective-ruby-core/blob/main/CHANGELOG.md
|
56
|
+
post_install_message:
|
57
|
+
rdoc_options: []
|
58
|
+
require_paths:
|
59
|
+
- lib
|
60
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: 2.6.0
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
requirements: []
|
71
|
+
rubygems_version: 3.4.10
|
72
|
+
signing_key:
|
73
|
+
specification_version: 4
|
74
|
+
summary: Selective Ruby Client Core
|
75
|
+
test_files: []
|