specguard-ruby 0.3.30 → 0.3.32
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/README.md +19 -5
- data/lib/specguard/client/configuration.rb +15 -1
- data/lib/specguard/client/delivery_warning.rb +43 -0
- data/lib/specguard/client/ingest_cli.rb +1 -2
- data/lib/specguard/client/transport.rb +68 -5
- data/lib/specguard/minitest/reporter.rb +5 -10
- data/lib/specguard/rspec/formatter.rb +5 -10
- data/lib/specguard/version.rb +1 -1
- data/script/bump-version.sh +2 -2
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4e2f8dd966880d2075d3989184c5f9df89d702445edcdf88583c0b8976021a46
|
|
4
|
+
data.tar.gz: 4e5d985eda3fb38fc4832412585f6f389a5d9429c915bc9797d02d349576a713
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dfc1753a08f2e9c713c61ed12ac18b557f5e32fbaa3577735383ac6adea9bf237ff6374baf6fe276e6735c0e2da38344574c2ab6b02de1a8f42fe41c3fc51aa9
|
|
7
|
+
data.tar.gz: eca2722d9dcfa6117c21ee589b2b2afdfa422c9983a49d66ee80974574e33f8163f10c2ccab65345badef6dec01d94ad25b436980d5d87beb81b224c3c2c6d1b
|
data/README.md
CHANGED
|
@@ -474,6 +474,15 @@ export SPECGUARD_API_KEY=… # from your repository's settings
|
|
|
474
474
|
export SPECGUARD_TIMEOUT=10 # optional; seconds, applied to connect and read
|
|
475
475
|
```
|
|
476
476
|
|
|
477
|
+
**Using an `sga_` agent key? Also set `SPECGUARD_REPOSITORY_ID`.** An agent key
|
|
478
|
+
covers a set of repositories, so the key cannot say which one a run belongs to;
|
|
479
|
+
the request has to. With `SPECGUARD_REPOSITORY_ID=42` the run is POSTed to
|
|
480
|
+
`<endpoint>/api/v1/repositories/42/ingest` instead of `<endpoint>/api/v1/ingest`
|
|
481
|
+
(`config.repository_id` in Ruby). Under a repository `sgk_` key leave it unset —
|
|
482
|
+
if you do set it, the server ignores it. The id may contain only letters, digits,
|
|
483
|
+
`-` and `_`; anything else is refused before a request is made. A `404` with an
|
|
484
|
+
id configured means no repository with that id is available to the key.
|
|
485
|
+
|
|
477
486
|
```ruby
|
|
478
487
|
# ...or in Ruby, if you would rather not use the environment
|
|
479
488
|
SpecGuard.configure do |config|
|
|
@@ -515,6 +524,7 @@ offered to the endpoint and not accepted — so the run can be replayed later wi
|
|
|
515
524
|
```
|
|
516
525
|
SpecGuard: could not deliver test telemetry (HTTP 401 — the API key was not
|
|
517
526
|
accepted). Falling back to log/test_results.jsonl; the test run is unaffected.
|
|
527
|
+
Once the delivery is fixed, replay this run with: bundle exec specguard-ingest log/test_results.jsonl
|
|
518
528
|
```
|
|
519
529
|
|
|
520
530
|
**That line carries the endpoint's own words when it has any.** A `400` refusal
|
|
@@ -527,7 +537,8 @@ SpecGuard: could not deliver test telemetry (HTTP 400 — the endpoint rejected
|
|
|
527
537
|
the payload — spec 3 (spec/orders_spec.rb:9): line_number is required and must
|
|
528
538
|
be a positive integer; spec 7 (spec/orders_spec.rb:31): outcome must be one of
|
|
529
539
|
passed, failed, pending). Falling back to log/test_results.jsonl; the test run
|
|
530
|
-
is unaffected.
|
|
540
|
+
is unaffected. Once the delivery is fixed, replay this run with: bundle exec
|
|
541
|
+
specguard-ingest log/test_results.jsonl
|
|
531
542
|
```
|
|
532
543
|
|
|
533
544
|
It stays **one** line whatever comes back. A systemic problem can have the
|
|
@@ -628,8 +639,9 @@ specguard-ingest: lines 1, 2 carried ci_run_id 17442 and each came back with
|
|
|
628
639
|
test_run_id 41f2c9b8 — the endpoint folded them onto one run
|
|
629
640
|
```
|
|
630
641
|
|
|
631
|
-
It reads the same `SPECGUARD_ENDPOINT`, `SPECGUARD_API_KEY`
|
|
632
|
-
`
|
|
642
|
+
It reads the same `SPECGUARD_ENDPOINT`, `SPECGUARD_API_KEY`, `SPECGUARD_REPOSITORY_ID`
|
|
643
|
+
(for `sga_` agent keys — a replay queue written under one is re-delivered to the same
|
|
644
|
+
repository-scoped route) and `SPECGUARD_TIMEOUT` the formatter does, and sends each line through the same
|
|
633
645
|
transport — so a run that was refused for a rotated key appears on the platform
|
|
634
646
|
once the secret is fixed, with the shard folding described in
|
|
635
647
|
[If you shard your suite](#if-you-shard-your-suite) applying exactly as it would
|
|
@@ -1212,9 +1224,11 @@ export SPECGUARD_ENDPOINT=https://specguard.internal.example.com
|
|
|
1212
1224
|
nothing any other formatter wrote, is read or forwarded.
|
|
1213
1225
|
- **No environment.** SpecGuard's own code reads a fixed list of variables and
|
|
1214
1226
|
no others: the ones named in the envelope table above, which fill
|
|
1215
|
-
`commit_sha`, `branch`, `ci_run_id` and `shard_id`; plus
|
|
1227
|
+
`commit_sha`, `branch`, `ci_run_id` and `shard_id`; plus five that configure
|
|
1216
1228
|
the gem itself rather than describing your suite — `SPECGUARD_ENDPOINT` (where
|
|
1217
|
-
to send the run), `
|
|
1229
|
+
to send the run), `SPECGUARD_REPOSITORY_ID` (which repository an `sga_` agent
|
|
1230
|
+
key's run belongs to — sent as a path segment of the request URL, nothing else),
|
|
1231
|
+
`SPECGUARD_OUTPUT_PATH` (where to write the replay queue —
|
|
1218
1232
|
runs the endpoint refused or could not be reached for), `SPECGUARD_LOCAL_OUTPUT_PATH`
|
|
1219
1233
|
(where to write the local development record when there is no key),
|
|
1220
1234
|
`SPECGUARD_TIMEOUT` (how long to wait), and
|
|
@@ -318,6 +318,7 @@ module SpecGuard
|
|
|
318
318
|
LOCAL_OUTPUT_PATH_KEYS = %w[SPECGUARD_LOCAL_OUTPUT_PATH].freeze
|
|
319
319
|
ENDPOINT_KEYS = %w[SPECGUARD_ENDPOINT].freeze
|
|
320
320
|
API_KEY_KEYS = %w[SPECGUARD_API_KEY].freeze
|
|
321
|
+
REPOSITORY_ID_KEYS = %w[SPECGUARD_REPOSITORY_ID].freeze
|
|
321
322
|
TIMEOUT_KEYS = %w[SPECGUARD_TIMEOUT].freeze
|
|
322
323
|
|
|
323
324
|
# The commit the suite ran against. `nil` when nothing said.
|
|
@@ -344,7 +345,9 @@ module SpecGuard
|
|
|
344
345
|
attr_accessor :local_output_path
|
|
345
346
|
# The SpecGuard installation to POST to, scheme and host only:
|
|
346
347
|
# `https://specguard.example.com`. The `/api/v1/ingest` path is the
|
|
347
|
-
# platform's contract, not a setting
|
|
348
|
+
# platform's contract, not a setting — with one exception: when
|
|
349
|
+
# {#repository_id} is set the path is `/api/v1/repositories/<id>/ingest`
|
|
350
|
+
# instead. `nil` when nothing said.
|
|
348
351
|
attr_accessor :endpoint
|
|
349
352
|
# The repository's SpecGuard API key. Present means "deliver over HTTP".
|
|
350
353
|
#
|
|
@@ -354,6 +357,16 @@ module SpecGuard
|
|
|
354
357
|
# nobody can retroactively fix. Send whatever is configured and let a 401
|
|
355
358
|
# be the answer.
|
|
356
359
|
attr_accessor :api_key
|
|
360
|
+
# The repository the run belongs to, sent as a path segment
|
|
361
|
+
# (`/api/v1/repositories/<id>/ingest`). Needed by an `sga_` agent key,
|
|
362
|
+
# which covers a set of repositories and so cannot name the run's
|
|
363
|
+
# repository itself; under an `sgk_` key the platform ignores it. `nil`
|
|
364
|
+
# when unset or blank, which keeps the plain `/api/v1/ingest` path.
|
|
365
|
+
#
|
|
366
|
+
# Deliberately **not** format-checked against the key prefix or a numeric
|
|
367
|
+
# shape (same reasoning as {#api_key}): the platform's 400/403/404 is the
|
|
368
|
+
# answer. {Transport} only refuses ids that are unsafe as a path segment.
|
|
369
|
+
attr_accessor :repository_id
|
|
357
370
|
# Seconds, applied to opening the connection and to reading the response.
|
|
358
371
|
attr_accessor :timeout
|
|
359
372
|
|
|
@@ -374,6 +387,7 @@ module SpecGuard
|
|
|
374
387
|
@local_output_path = first_present(env, LOCAL_OUTPUT_PATH_KEYS) || DEFAULT_LOCAL_OUTPUT_PATH
|
|
375
388
|
@endpoint = first_present(env, ENDPOINT_KEYS)
|
|
376
389
|
@api_key = first_present(env, API_KEY_KEYS)
|
|
390
|
+
@repository_id = first_present(env, REPOSITORY_ID_KEYS)
|
|
377
391
|
@timeout = seconds(first_present(env, TIMEOUT_KEYS)) || DEFAULT_TIMEOUT_SECONDS
|
|
378
392
|
end
|
|
379
393
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SpecGuard
|
|
4
|
+
module Client
|
|
5
|
+
# The two clauses that finish a delivery-failure warning line, shared by
|
|
6
|
+
# the RSpec formatter and the Minitest reporter.
|
|
7
|
+
#
|
|
8
|
+
# Both clients print the same sentence after their own prefix and status
|
|
9
|
+
# clause: where the run ended up, and (when it was saved) the command that
|
|
10
|
+
# gets it back. They used to carry two inline copies kept equal
|
|
11
|
+
# "word-for-word" by hand, and the pair had already been co-edited in
|
|
12
|
+
# separate commits (SPGD-1139, SPGD-1413). One definition here means a
|
|
13
|
+
# wording change cannot reach one client and miss the other.
|
|
14
|
+
#
|
|
15
|
+
# What stays in each client: the prefix, the warning budget (`@warned`),
|
|
16
|
+
# and the `append` that decides whether `error` is nil. Those differ by
|
|
17
|
+
# design; only the clause text is shared.
|
|
18
|
+
module DeliveryWarning
|
|
19
|
+
module_function
|
|
20
|
+
|
|
21
|
+
# Report, not promise. Composed after the write so it can tell the
|
|
22
|
+
# operator which of the two things actually happened. `error` is the
|
|
23
|
+
# client's `append` answer: `nil` means the queue took the run (the
|
|
24
|
+
# success arm, {#replay_clause}); anything else means both sinks are
|
|
25
|
+
# gone and the run really is lost (SPGD-1413).
|
|
26
|
+
def sink_clause(path, error)
|
|
27
|
+
return replay_clause(path) if error.nil?
|
|
28
|
+
|
|
29
|
+
"The replay queue #{path} could not be written either " \
|
|
30
|
+
"(#{error.class}: #{error.message}), so this run's telemetry was lost."
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# The success arm (SPGD-1139): where the run sits AND the command that
|
|
34
|
+
# gets it back, fix-the-delivery first and replay after — the order the
|
|
35
|
+
# ingest CLI's own doctrine describes. The lost-run arm deliberately
|
|
36
|
+
# names no command: with no queue written there is nothing to replay.
|
|
37
|
+
def replay_clause(path)
|
|
38
|
+
"Falling back to #{path}; the test run is unaffected. " \
|
|
39
|
+
"Once the delivery is fixed, replay this run with: bundle exec specguard-ingest #{path}"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -739,8 +739,7 @@ module SpecGuard
|
|
|
739
739
|
raise UsageError, "no endpoint is configured (set SPECGUARD_ENDPOINT)" if blank?(configuration.endpoint)
|
|
740
740
|
raise UsageError, "no API key is configured (set SPECGUARD_API_KEY)" if blank?(configuration.api_key)
|
|
741
741
|
|
|
742
|
-
transport = Transport.
|
|
743
|
-
timeout: configuration.timeout)
|
|
742
|
+
transport = Transport.from_configuration(configuration)
|
|
744
743
|
# Asked once, here, so a malformed `SPECGUARD_ENDPOINT` is one exit 2
|
|
745
744
|
# rather than N identical `:failed` lines. `Transport#deliver` would
|
|
746
745
|
# otherwise swallow the same `ArgumentError` once per line and report a
|
|
@@ -127,6 +127,21 @@ module SpecGuard
|
|
|
127
127
|
# Part of the platform's contract, so it is not configurable — the
|
|
128
128
|
# `endpoint` setting is the installation's address and nothing more.
|
|
129
129
|
PATH = "/api/v1/ingest"
|
|
130
|
+
|
|
131
|
+
# The repository-scoped form of the same endpoint (`post "ingest"` nested
|
|
132
|
+
# under `resources :repositories`). It exists because an `sga_` agent key
|
|
133
|
+
# covers a SET of repositories, so the credential cannot name the run's
|
|
134
|
+
# repository and the request has to, as a path segment. `%s` is only ever
|
|
135
|
+
# filled with an id that passed {REPOSITORY_ID_PATTERN}. Under an `sgk_`
|
|
136
|
+
# key the server ignores the segment, so {PATH} stays the default.
|
|
137
|
+
REPOSITORY_PATH = "/api/v1/repositories/%s/ingest"
|
|
138
|
+
|
|
139
|
+
# What a repository id may look like before it becomes a path segment.
|
|
140
|
+
# Deliberately not "digits": the platform's ids are numeric today and the
|
|
141
|
+
# gem must not bake that in. Dots are excluded outright, which is what
|
|
142
|
+
# refuses `..` (percent-escaping would not neutralise a dot segment), and
|
|
143
|
+
# `/`, `?`, `#` can never survive as path structure.
|
|
144
|
+
REPOSITORY_ID_PATTERN = /\A[0-9A-Za-z_-]+\z/
|
|
130
145
|
CONTENT_TYPE = "application/json"
|
|
131
146
|
USER_AGENT = "specguard-ruby/#{SpecGuard::VERSION}"
|
|
132
147
|
CONTENT_ENCODING = "gzip"
|
|
@@ -167,7 +182,12 @@ module SpecGuard
|
|
|
167
182
|
# same reason turned around: a 202 whose body will not parse is still an
|
|
168
183
|
# acceptance, and relabelling it would tell the operator something untrue
|
|
169
184
|
# about a run the platform has already stored. See {#test_run_id}.
|
|
170
|
-
|
|
185
|
+
#
|
|
186
|
+
# `repository_scoped` is true when the request went to the repository-
|
|
187
|
+
# scoped route. It only changes the 404 sentence: under that route a 404
|
|
188
|
+
# means no repository with that id is available to this key, not a wrong
|
|
189
|
+
# URL. Nil/false (every pre-existing `Result.new`) keeps today's wording.
|
|
190
|
+
Result = Struct.new(:outcome, :code, :error, :reasons, :body, :repository_scoped, keyword_init: true) do
|
|
171
191
|
# The status codes worth spelling out, because each implies a different
|
|
172
192
|
# thing for the reader to *do*. A 401 means "rotate or fix the key"; a
|
|
173
193
|
# 400 means "the payload this gem built was refused", which is a bug
|
|
@@ -193,6 +213,10 @@ module SpecGuard
|
|
|
193
213
|
# tells the reader how widespread it is.
|
|
194
214
|
MAX_RENDERED_REASONS = 3
|
|
195
215
|
|
|
216
|
+
# The 404 sentence when a repository id was sent. See `repository_scoped`.
|
|
217
|
+
REPOSITORY_NOT_FOUND_ADVICE =
|
|
218
|
+
"no repository with that id is available to this API key — check SPECGUARD_REPOSITORY_ID"
|
|
219
|
+
|
|
196
220
|
# Ceiling on the spelled-out reasons, before the `and N more` tail,
|
|
197
221
|
# which is appended afterwards so the count can never be the thing that
|
|
198
222
|
# gets truncated away.
|
|
@@ -224,13 +248,19 @@ module SpecGuard
|
|
|
224
248
|
def reason
|
|
225
249
|
case outcome
|
|
226
250
|
when :success then nil
|
|
227
|
-
when :rejected then [+"HTTP #{code}",
|
|
251
|
+
when :rejected then [+"HTTP #{code}", advice, rendered_reasons].compact.join(" — ")
|
|
228
252
|
else "#{error.class}: #{error.message}"
|
|
229
253
|
end
|
|
230
254
|
end
|
|
231
255
|
|
|
232
256
|
private
|
|
233
257
|
|
|
258
|
+
def advice
|
|
259
|
+
return REPOSITORY_NOT_FOUND_ADVICE if code == 404 && repository_scoped
|
|
260
|
+
|
|
261
|
+
ADVICE[code]
|
|
262
|
+
end
|
|
263
|
+
|
|
234
264
|
# The refusal, bounded and flattened to fit on the one line. `nil` when
|
|
235
265
|
# there is nothing readable to add, which is what keeps the bare
|
|
236
266
|
# `HTTP 400 — the endpoint rejected the payload` intact for every body
|
|
@@ -263,6 +293,21 @@ module SpecGuard
|
|
|
263
293
|
end
|
|
264
294
|
end
|
|
265
295
|
|
|
296
|
+
# Builds a transport from a {Configuration}, projecting the four settings
|
|
297
|
+
# every ingest writer forwards (endpoint, api_key, timeout, repository_id)
|
|
298
|
+
# so a new keyword is threaded in one place rather than in each writer.
|
|
299
|
+
#
|
|
300
|
+
# @param configuration [Configuration, #endpoint, #api_key, #timeout, #repository_id]
|
|
301
|
+
# @return [Transport]
|
|
302
|
+
def self.from_configuration(configuration)
|
|
303
|
+
new(
|
|
304
|
+
endpoint: configuration.endpoint,
|
|
305
|
+
api_key: configuration.api_key,
|
|
306
|
+
timeout: configuration.timeout,
|
|
307
|
+
repository_id: configuration.repository_id
|
|
308
|
+
)
|
|
309
|
+
end
|
|
310
|
+
|
|
266
311
|
# @param endpoint [String, nil] the installation's base URL. Any trailing
|
|
267
312
|
# slashes are dropped; a path prefix is preserved, so an installation
|
|
268
313
|
# behind `https://tools.example.com/specguard` works.
|
|
@@ -270,9 +315,15 @@ module SpecGuard
|
|
|
270
315
|
# @param timeout [Numeric, String, nil] seconds. Anything that is not a
|
|
271
316
|
# positive finite number falls back to the default rather than raising:
|
|
272
317
|
# a typo in `SPECGUARD_TIMEOUT` must not be able to fail a suite.
|
|
273
|
-
|
|
318
|
+
# @param repository_id [String, Integer, nil] when present, POST to the
|
|
319
|
+
# repository-scoped route an `sga_` agent key needs. nil/blank keeps the
|
|
320
|
+
# `sgk_` path byte-identical. An id outside {REPOSITORY_ID_PATTERN} makes
|
|
321
|
+
# {#uri} raise `ArgumentError`.
|
|
322
|
+
def initialize(endpoint:, api_key:, timeout: Configuration::DEFAULT_TIMEOUT_SECONDS, repository_id: nil)
|
|
274
323
|
@endpoint = endpoint
|
|
275
324
|
@api_key = api_key
|
|
325
|
+
@repository_id = repository_id.to_s.strip
|
|
326
|
+
@repository_id = nil if @repository_id.empty?
|
|
276
327
|
@timeout = sanitize_timeout(timeout)
|
|
277
328
|
end
|
|
278
329
|
|
|
@@ -301,7 +352,8 @@ module SpecGuard
|
|
|
301
352
|
return Result.new(outcome: :success, code: code, body: success_body(response)) if
|
|
302
353
|
response.is_a?(Net::HTTPSuccess)
|
|
303
354
|
|
|
304
|
-
Result.new(outcome: :rejected, code: code, reasons: refusal_reasons(response)
|
|
355
|
+
Result.new(outcome: :rejected, code: code, reasons: refusal_reasons(response),
|
|
356
|
+
repository_scoped: !@repository_id.nil?)
|
|
305
357
|
rescue ScriptError, StandardError => e
|
|
306
358
|
# Connection refused, DNS failure, TLS failure, open/read timeout, a
|
|
307
359
|
# malformed endpoint — one family, one shape. `ScriptError` is in the
|
|
@@ -427,7 +479,7 @@ module SpecGuard
|
|
|
427
479
|
base = @endpoint.to_s.strip
|
|
428
480
|
raise ArgumentError, "no endpoint is configured (set SPECGUARD_ENDPOINT)" if base.empty?
|
|
429
481
|
|
|
430
|
-
parsed = URI.parse(base.sub(%r{/+\z}, "") +
|
|
482
|
+
parsed = URI.parse(base.sub(%r{/+\z}, "") + ingest_path)
|
|
431
483
|
# `URI::HTTPS < URI::HTTP`, so this admits both and rejects the
|
|
432
484
|
# scheme-less `specguard.example.com` that `URI.parse` happily returns
|
|
433
485
|
# as a `URI::Generic` with a nil host — which `Net::HTTP` would then
|
|
@@ -438,6 +490,17 @@ module SpecGuard
|
|
|
438
490
|
parsed
|
|
439
491
|
end
|
|
440
492
|
|
|
493
|
+
def ingest_path
|
|
494
|
+
return PATH if @repository_id.nil?
|
|
495
|
+
|
|
496
|
+
unless REPOSITORY_ID_PATTERN.match?(@repository_id)
|
|
497
|
+
raise ArgumentError,
|
|
498
|
+
"SPECGUARD_REPOSITORY_ID must contain only letters, digits, '-' and '_', got #{@repository_id.inspect}"
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
format(REPOSITORY_PATH, @repository_id)
|
|
502
|
+
end
|
|
503
|
+
|
|
441
504
|
def sanitize_timeout(value)
|
|
442
505
|
parsed = Float(value, exception: false)
|
|
443
506
|
return Configuration::DEFAULT_TIMEOUT_SECONDS unless parsed&.finite? && parsed.positive?
|
|
@@ -9,6 +9,7 @@ require "fileutils"
|
|
|
9
9
|
# loads them on a minitest-only consumer's machine.
|
|
10
10
|
require_relative "../client"
|
|
11
11
|
require_relative "../client/configuration"
|
|
12
|
+
require_relative "../client/delivery_warning"
|
|
12
13
|
|
|
13
14
|
# The annotation lookup is the third framework-free half: what counts as a
|
|
14
15
|
# test's annotation is a property of the source line, not of the framework
|
|
@@ -244,11 +245,7 @@ module SpecGuard
|
|
|
244
245
|
|
|
245
246
|
def transport_for
|
|
246
247
|
require_relative "../client/transport"
|
|
247
|
-
SpecGuard::Client::Transport.
|
|
248
|
-
endpoint: @configuration.endpoint,
|
|
249
|
-
api_key: @configuration.api_key,
|
|
250
|
-
timeout: @configuration.timeout
|
|
251
|
-
)
|
|
248
|
+
SpecGuard::Client::Transport.from_configuration(@configuration)
|
|
252
249
|
end
|
|
253
250
|
|
|
254
251
|
def row_for(result)
|
|
@@ -403,12 +400,10 @@ module SpecGuard
|
|
|
403
400
|
end
|
|
404
401
|
|
|
405
402
|
# Report, not promise — see `#fall_back` for why that is the whole of
|
|
406
|
-
# this slice, and why the order above it did not move.
|
|
403
|
+
# this slice, and why the order above it did not move. The wording is
|
|
404
|
+
# shared with the RSpec formatter in {Client::DeliveryWarning}.
|
|
407
405
|
def sink_clause(path, error)
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
"The replay queue #{path} could not be written either " \
|
|
411
|
-
"(#{error.class}: #{error.message}), so this run's telemetry was lost."
|
|
406
|
+
SpecGuard::Client::DeliveryWarning.sink_clause(path, error)
|
|
412
407
|
end
|
|
413
408
|
|
|
414
409
|
# Once per process, like the formatter's `warn_once`: fifty failing
|
|
@@ -48,6 +48,7 @@ require "fileutils"
|
|
|
48
48
|
require_relative "../rspec"
|
|
49
49
|
require_relative "../client/configuration"
|
|
50
50
|
require_relative "../client/transport"
|
|
51
|
+
require_relative "../client/delivery_warning"
|
|
51
52
|
# Brings the linter's discovery chain with it (Scanner, Finding, Schema). That
|
|
52
53
|
# direction is safe — `specguard/rspec` does not require `rspec/core`, so the
|
|
53
54
|
# linter stays loadable without RSpec; it is only the reverse that would break
|
|
@@ -1037,11 +1038,7 @@ module SpecGuard
|
|
|
1037
1038
|
end
|
|
1038
1039
|
|
|
1039
1040
|
def transport_for(configuration)
|
|
1040
|
-
SpecGuard::Client::Transport.
|
|
1041
|
-
endpoint: configuration.endpoint,
|
|
1042
|
-
api_key: configuration.api_key,
|
|
1043
|
-
timeout: configuration.timeout
|
|
1044
|
-
)
|
|
1041
|
+
SpecGuard::Client::Transport.from_configuration(configuration)
|
|
1045
1042
|
end
|
|
1046
1043
|
|
|
1047
1044
|
def blank?(value)
|
|
@@ -1200,12 +1197,10 @@ module SpecGuard
|
|
|
1200
1197
|
|
|
1201
1198
|
# Report, not promise. Composed after the write so it can tell the operator
|
|
1202
1199
|
# which of the two things actually happened — see {#fall_back} for why that
|
|
1203
|
-
# is the whole of this slice, and why the order above it did not move.
|
|
1200
|
+
# is the whole of this slice, and why the order above it did not move. The
|
|
1201
|
+
# wording is shared with the Minitest reporter in {Client::DeliveryWarning}.
|
|
1204
1202
|
def sink_clause(path, error)
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
"The replay queue #{path} could not be written either " \
|
|
1208
|
-
"(#{error.class}: #{error.message}), so this run's telemetry was lost."
|
|
1203
|
+
SpecGuard::Client::DeliveryWarning.sink_clause(path, error)
|
|
1209
1204
|
end
|
|
1210
1205
|
|
|
1211
1206
|
def emit_warning(line)
|
data/lib/specguard/version.rb
CHANGED
data/script/bump-version.sh
CHANGED
|
@@ -58,9 +58,9 @@ if git rev-parse -q --verify "refs/tags/v$NEW_VERSION" >/dev/null || \
|
|
|
58
58
|
exit 1
|
|
59
59
|
fi
|
|
60
60
|
if command -v curl >/dev/null 2>&1; then
|
|
61
|
-
if curl -fsS --max-time 10 "https://rubygems.org/api/v1/versions/specguard-
|
|
61
|
+
if curl -fsS --max-time 10 "https://rubygems.org/api/v1/versions/specguard-ruby.json" 2>/dev/null \
|
|
62
62
|
| grep -q "\"number\":\"$NEW_VERSION\""; then
|
|
63
|
-
echo "Error: specguard-
|
|
63
|
+
echo "Error: specguard-ruby $NEW_VERSION is already published on RubyGems"
|
|
64
64
|
exit 1
|
|
65
65
|
fi
|
|
66
66
|
fi
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: specguard-ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.32
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- specguard Agent
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-07 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: json
|
|
@@ -52,6 +52,7 @@ files:
|
|
|
52
52
|
- lib/specguard/client/annotation_scanner.rb
|
|
53
53
|
- lib/specguard/client/cli.rb
|
|
54
54
|
- lib/specguard/client/configuration.rb
|
|
55
|
+
- lib/specguard/client/delivery_warning.rb
|
|
55
56
|
- lib/specguard/client/file_selector.rb
|
|
56
57
|
- lib/specguard/client/finding.rb
|
|
57
58
|
- lib/specguard/client/ingest_cli.rb
|