rspecq 0.3.0 → 0.4.0
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/CHANGELOG.md +8 -0
- data/README.md +12 -0
- data/bin/rspecq +8 -0
- data/lib/rspecq/queue.rb +32 -2
- data/lib/rspecq/reporter.rb +8 -1
- data/lib/rspecq/version.rb +1 -1
- data/lib/rspecq/worker.rb +11 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ae9ce385f55d300aa1c6d50b75ffe5bb91b28783e834009dc308d3e52856f786
|
4
|
+
data.tar.gz: 13cf6b430cb88a14040442a94f0df5d2831f263ae1205a6b0c6e228c5bc3aefe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a98eadc353fbc02963228503b92d88cdc04414c037d5728ebad471764b202967a5b535e34a9533abb8879ccfe20b88ff2561f8e122433b0f6deb1333ee9c22cf
|
7
|
+
data.tar.gz: 58af317b915a83bb8f0458dd25ab7a2b1aa31ee3835e3ad05082a6478f68d74681c0d040d9455b11135e8952bee2aadde70a344d1f25301af0a604a4e996b746
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,14 @@ Breaking changes are prefixed with a "[BREAKING]" label.
|
|
4
4
|
|
5
5
|
## master (unreleased)
|
6
6
|
|
7
|
+
## 0.4.0 (2020-10-07)
|
8
|
+
|
9
|
+
### Added
|
10
|
+
|
11
|
+
- Builds can be configured to terminate after a specified number of failures,
|
12
|
+
using the `--fail-fast` option.
|
13
|
+
|
14
|
+
|
7
15
|
## 0.3.0 (2020-10-05)
|
8
16
|
|
9
17
|
### Added
|
data/README.md
CHANGED
@@ -28,6 +28,7 @@ and [ci-queue](https://github.com/Shopify/ci-queue).
|
|
28
28
|
- Sentry integration for monitoring build-level events. See [*Sentry integration*](#sentry-integration).
|
29
29
|
- [PLANNED] StatsD integration for various build-level metrics and insights.
|
30
30
|
See [#2](https://github.com/skroutz/rspecq/issues/2).
|
31
|
+
- Automatic termination of builds after a certain amount of failures. See [*Fail-fast*](#fail-fast).
|
31
32
|
|
32
33
|
## Usage
|
33
34
|
|
@@ -73,6 +74,7 @@ OPTIONS:
|
|
73
74
|
Exits with a non-zero status code if there were any failures.
|
74
75
|
--report-timeout N Fail if build is not finished after N seconds. Only applicable if --report is enabled (default: 3600).
|
75
76
|
--max-requeues N Retry failed examples up to N times before considering them legit failures (default: 3).
|
77
|
+
--fail-fast N Abort build with a non-zero status code after N failed examples.
|
76
78
|
-h, --help Show this message.
|
77
79
|
-v, --version Print the version and exit.
|
78
80
|
```
|
@@ -133,6 +135,16 @@ final report.
|
|
133
135
|
Flaky tests are also detected and printed as such in the final report. They are
|
134
136
|
also emitted to Sentry (see [Sentry integration](#sentry-integration)).
|
135
137
|
|
138
|
+
### Fail-fast
|
139
|
+
|
140
|
+
In order to prevent large suites running for a long time with a lot of
|
141
|
+
failures, a threshold can be set to control the number of failed examples that
|
142
|
+
will render the build unsuccessful. This is in par with RSpec's
|
143
|
+
[--fail-fast](https://relishapp.com/rspec/rspec-core/docs/command-line/fail-fast-option).
|
144
|
+
|
145
|
+
This feature is disabled by default, and can be controlled via the
|
146
|
+
`--fail-fast` command line option.
|
147
|
+
|
136
148
|
### Worker failures
|
137
149
|
|
138
150
|
It's not uncommon for CI processes to encounter unrecoverable failures for
|
data/bin/rspecq
CHANGED
@@ -5,6 +5,7 @@ require "rspecq"
|
|
5
5
|
DEFAULT_REDIS_HOST = "127.0.0.1"
|
6
6
|
DEFAULT_REPORT_TIMEOUT = 3600 # 1 hour
|
7
7
|
DEFAULT_MAX_REQUEUES = 3
|
8
|
+
DEFAULT_FAIL_FAST = 0
|
8
9
|
|
9
10
|
def env_set?(var)
|
10
11
|
["1", "true"].include?(ENV[var])
|
@@ -83,6 +84,11 @@ OptionParser.new do |o|
|
|
83
84
|
opts[:max_requeues] = v
|
84
85
|
end
|
85
86
|
|
87
|
+
o.on("--fail-fast N", Integer, "Abort build with a non-zero status code " \
|
88
|
+
"after N failed examples." ) do |v|
|
89
|
+
opts[:fail_fast] = v
|
90
|
+
end
|
91
|
+
|
86
92
|
o.on_tail("-h", "--help", "Show this message.") do
|
87
93
|
puts o
|
88
94
|
exit
|
@@ -103,6 +109,7 @@ opts[:report] ||= env_set?("RSPECQ_REPORT")
|
|
103
109
|
opts[:report_timeout] ||= Integer(ENV["RSPECQ_REPORT_TIMEOUT"] || DEFAULT_REPORT_TIMEOUT)
|
104
110
|
opts[:max_requeues] ||= Integer(ENV["RSPECQ_MAX_REQUEUES"] || DEFAULT_MAX_REQUEUES)
|
105
111
|
opts[:redis_url] ||= ENV["RSPECQ_REDIS_URL"]
|
112
|
+
opts[:fail_fast] ||= Integer(ENV["RSPECQ_FAIL_FAST"] || DEFAULT_FAIL_FAST)
|
106
113
|
|
107
114
|
raise OptionParser::MissingArgument.new(:build) if opts[:build].nil?
|
108
115
|
raise OptionParser::MissingArgument.new(:worker) if !opts[:report] && opts[:worker].nil?
|
@@ -134,5 +141,6 @@ else
|
|
134
141
|
worker.populate_timings = opts[:timings]
|
135
142
|
worker.file_split_threshold = opts[:file_split_threshold]
|
136
143
|
worker.max_requeues = opts[:max_requeues]
|
144
|
+
worker.fail_fast = opts[:fail_fast]
|
137
145
|
worker.work
|
138
146
|
end
|
data/lib/rspecq/queue.rb
CHANGED
@@ -77,8 +77,9 @@ module RSpecQ
|
|
77
77
|
end
|
78
78
|
|
79
79
|
# NOTE: jobs will be processed from head to tail (lpop)
|
80
|
-
def publish(jobs)
|
80
|
+
def publish(jobs, fail_fast = 0)
|
81
81
|
@redis.multi do
|
82
|
+
@redis.hset(key_queue_config, 'fail_fast', fail_fast)
|
82
83
|
@redis.rpush(key_queue_unprocessed, jobs)
|
83
84
|
@redis.set(key_queue_status, STATUS_READY)
|
84
85
|
end.first
|
@@ -232,7 +233,9 @@ module RSpecQ
|
|
232
233
|
# after being retried). Must be called after the build is complete,
|
233
234
|
# otherwise an exception will be raised.
|
234
235
|
def flaky_jobs
|
235
|
-
|
236
|
+
if !exhausted? && !build_failed_fast?
|
237
|
+
raise "Queue is not yet exhausted"
|
238
|
+
end
|
236
239
|
|
237
240
|
requeued = @redis.hkeys(key_requeues)
|
238
241
|
|
@@ -241,11 +244,38 @@ module RSpecQ
|
|
241
244
|
requeued - @redis.hkeys(key_failures)
|
242
245
|
end
|
243
246
|
|
247
|
+
# Returns the number of failures that will trigger the build to fail-fast.
|
248
|
+
# Returns 0 if this feature is disabled and nil if the Queue is not yet
|
249
|
+
# published
|
250
|
+
def fail_fast
|
251
|
+
return nil unless published?
|
252
|
+
|
253
|
+
@fail_fast ||= Integer(@redis.hget(key_queue_config, 'fail_fast'))
|
254
|
+
end
|
255
|
+
|
256
|
+
# Returns true if the number of failed tests, has surpassed the threshold
|
257
|
+
# to render the run unsuccessful and the build should be terminated.
|
258
|
+
def build_failed_fast?
|
259
|
+
if fail_fast.nil? || fail_fast.zero?
|
260
|
+
return false
|
261
|
+
end
|
262
|
+
|
263
|
+
@redis.multi do
|
264
|
+
@redis.hlen(key_failures)
|
265
|
+
@redis.hlen(key_errors)
|
266
|
+
end.inject(:+) >= fail_fast
|
267
|
+
end
|
268
|
+
|
244
269
|
# redis: STRING [STATUS_INITIALIZING, STATUS_READY]
|
245
270
|
def key_queue_status
|
246
271
|
key("queue", "status")
|
247
272
|
end
|
248
273
|
|
274
|
+
# redis: HASH<config_key => config_value>
|
275
|
+
def key_queue_config
|
276
|
+
key("queue", "config")
|
277
|
+
end
|
278
|
+
|
249
279
|
# redis: LIST<job>
|
250
280
|
def key_queue_unprocessed
|
251
281
|
key("queue", "unprocessed")
|
data/lib/rspecq/reporter.rb
CHANGED
@@ -41,7 +41,7 @@ module RSpecQ
|
|
41
41
|
puts failure_formatted(rspec_output)
|
42
42
|
end
|
43
43
|
|
44
|
-
|
44
|
+
unless @queue.exhausted? || @queue.build_failed_fast?
|
45
45
|
sleep 1
|
46
46
|
next
|
47
47
|
end
|
@@ -83,6 +83,13 @@ module RSpecQ
|
|
83
83
|
end
|
84
84
|
|
85
85
|
summary = ""
|
86
|
+
if @queue.build_failed_fast?
|
87
|
+
summary << "\n\n"
|
88
|
+
summary << "The limit of #{@queue.fail_fast} failures has been reached\n"
|
89
|
+
summary << "Aborting..."
|
90
|
+
summary << "\n"
|
91
|
+
end
|
92
|
+
|
86
93
|
summary << failed_examples_section if !failures.empty?
|
87
94
|
|
88
95
|
errors.each { |_job, msg| summary << msg }
|
data/lib/rspecq/version.rb
CHANGED
data/lib/rspecq/worker.rb
CHANGED
@@ -40,12 +40,19 @@ module RSpecQ
|
|
40
40
|
# Defaults to 3
|
41
41
|
attr_accessor :max_requeues
|
42
42
|
|
43
|
+
# Stop the execution after N failed tests. Do not stop at any point when
|
44
|
+
# set to 0.
|
45
|
+
#
|
46
|
+
# Defaults to 0
|
47
|
+
attr_accessor :fail_fast
|
48
|
+
|
43
49
|
attr_reader :queue
|
44
50
|
|
45
51
|
def initialize(build_id:, worker_id:, redis_opts:)
|
46
52
|
@build_id = build_id
|
47
53
|
@worker_id = worker_id
|
48
54
|
@queue = Queue.new(build_id, worker_id, redis_opts)
|
55
|
+
@fail_fast = 0
|
49
56
|
@files_or_dirs_to_run = "spec"
|
50
57
|
@populate_timings = false
|
51
58
|
@file_split_threshold = 999999
|
@@ -69,6 +76,8 @@ module RSpecQ
|
|
69
76
|
# to `requeue_lost_job` inside the work loop
|
70
77
|
update_heartbeat
|
71
78
|
|
79
|
+
return if queue.build_failed_fast?
|
80
|
+
|
72
81
|
lost = queue.requeue_lost_job
|
73
82
|
puts "Requeued lost job: #{lost}" if lost
|
74
83
|
|
@@ -121,7 +130,7 @@ module RSpecQ
|
|
121
130
|
|
122
131
|
timings = queue.timings
|
123
132
|
if timings.empty?
|
124
|
-
q_size = queue.publish(files_to_run.shuffle)
|
133
|
+
q_size = queue.publish(files_to_run.shuffle, fail_fast)
|
125
134
|
log_event(
|
126
135
|
"No timings found! Published queue in random order (size=#{q_size})",
|
127
136
|
"warning"
|
@@ -160,7 +169,7 @@ module RSpecQ
|
|
160
169
|
# sort jobs based on their timings (slowest to be processed first)
|
161
170
|
jobs = jobs.sort_by { |_j, t| -t }.map(&:first)
|
162
171
|
|
163
|
-
puts "Published queue (size=#{queue.publish(jobs)})"
|
172
|
+
puts "Published queue (size=#{queue.publish(jobs, fail_fast)})"
|
164
173
|
end
|
165
174
|
|
166
175
|
private
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rspecq
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Agis Anastasopoulos
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-10-
|
11
|
+
date: 2020-10-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec-core
|