specguard-ruby 0.3.1
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/.github/workflows/ci.yml +49 -0
- data/.github/workflows/release.yml +100 -0
- data/LICENSE +21 -0
- data/README.md +1036 -0
- data/Rakefile +28 -0
- data/assets/built-with-yatfa.png +0 -0
- data/bin/specguard-ingest +40 -0
- data/bin/specguard-lint +34 -0
- data/lib/minitest/specguard_plugin.rb +25 -0
- data/lib/specguard/minitest/reporter.rb +244 -0
- data/lib/specguard/rspec/annotation_lookup.rb +363 -0
- data/lib/specguard/rspec/annotation_scanner.rb +190 -0
- data/lib/specguard/rspec/cli.rb +375 -0
- data/lib/specguard/rspec/configuration.rb +472 -0
- data/lib/specguard/rspec/file_selector.rb +276 -0
- data/lib/specguard/rspec/finding.rb +74 -0
- data/lib/specguard/rspec/formatter.rb +1110 -0
- data/lib/specguard/rspec/ingest_cli.rb +942 -0
- data/lib/specguard/rspec/ingest_reporter.rb +280 -0
- data/lib/specguard/rspec/json_reporter.rb +169 -0
- data/lib/specguard/rspec/linter.rb +156 -0
- data/lib/specguard/rspec/payload_normalizer.rb +143 -0
- data/lib/specguard/rspec/scanner.rb +235 -0
- data/lib/specguard/rspec/schemas/open-test-intent.v1.json +15 -0
- data/lib/specguard/rspec/transport.rb +449 -0
- data/lib/specguard/rspec/validator_backend.rb +1450 -0
- data/lib/specguard/rspec/version.rb +14 -0
- data/lib/specguard/rspec.rb +71 -0
- data/lib/specguard/version.rb +11 -0
- data/script/bump-version.sh +128 -0
- metadata +96 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Back-compat alias. The version number itself lives in `SpecGuard::VERSION`
|
|
4
|
+
# (lib/specguard/version.rb) since the gem became `specguard-ruby`; this
|
|
5
|
+
# constant is kept because released `specguard-rspec` versions exposed it and
|
|
6
|
+
# a consumer reaching for it should not break on the rename. It is the same
|
|
7
|
+
# number, not a second one — `script/bump-version.sh` edits only the source.
|
|
8
|
+
require_relative "../version"
|
|
9
|
+
|
|
10
|
+
module SpecGuard
|
|
11
|
+
module RSpec
|
|
12
|
+
VERSION = SpecGuard::VERSION
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "rspec/version"
|
|
4
|
+
|
|
5
|
+
module SpecGuard
|
|
6
|
+
module RSpec
|
|
7
|
+
# The Ruby client for SpecGuard.
|
|
8
|
+
#
|
|
9
|
+
# The linter is complete end to end: it finds `@intent:` annotations in
|
|
10
|
+
# spec files, captures each payload string-aware, normalizes PROTOCOL.md
|
|
11
|
+
# §1's permissive syntax into strict JSON, validates the result against the
|
|
12
|
+
# vendored OpenTestIntent schema, and reports violations in the reference
|
|
13
|
+
# tool's own grammar under the 0/1/2 exit contract (see {CLI}).
|
|
14
|
+
#
|
|
15
|
+
# The RSpec formatter (`SpecGuard::RSpecFormatter`) is the other half of
|
|
16
|
+
# the client, and it is deliberately **not** required from here: `rspec` is
|
|
17
|
+
# a development dependency of this gem, not a runtime one, and
|
|
18
|
+
# `bin/specguard-lint` loads this file on machines that may have no RSpec
|
|
19
|
+
# at all. Requiring `rspec/core` from this chain would turn a missing test
|
|
20
|
+
# framework into a broken linter. Load it by its own path when you want it
|
|
21
|
+
# — `require "specguard/rspec/formatter"` — and see that file for the
|
|
22
|
+
# opt-in wiring.
|
|
23
|
+
class Error < StandardError; end
|
|
24
|
+
|
|
25
|
+
# A source line could not be scanned (unterminated string or object
|
|
26
|
+
# literal). Internal to the scanner: it is caught and turned into a
|
|
27
|
+
# {Finding}'s `problem` rather than reaching a caller.
|
|
28
|
+
class ScanError < Error; end
|
|
29
|
+
|
|
30
|
+
# The tool was invoked in a way that cannot work — `--changed` outside a git
|
|
31
|
+
# repository, say. Distinct from "the annotations are bad": the exit-code
|
|
32
|
+
# contract makes this a 2, and it is typed here so the CLI can map it
|
|
33
|
+
# without re-deriving the distinction from an error message.
|
|
34
|
+
class UsageError < Error; end
|
|
35
|
+
|
|
36
|
+
# The opt-in Go validator backend could not produce a verdict — the binary
|
|
37
|
+
# named by `SPECGUARD_VALIDATE_INTENT` is missing, will not execute, exited
|
|
38
|
+
# with a code that is not a verdict, or emitted something this cannot read
|
|
39
|
+
# as a report.
|
|
40
|
+
#
|
|
41
|
+
# Typed separately from {UsageError} because the two are different
|
|
42
|
+
# accusations ("you invoked me wrongly" vs "the tool I was told to use is
|
|
43
|
+
# broken"), and rescued *beside* it in {CLI#run} because the answer to both
|
|
44
|
+
# is the same and non-negotiable: exit 2, never 1. See {ValidatorBackend}.
|
|
45
|
+
class ValidatorError < Error; end
|
|
46
|
+
|
|
47
|
+
# The vendored canonical OpenTestIntent schema, copied byte-for-byte from
|
|
48
|
+
# open-test-intent so this gem has **no cross-repo runtime dependency**.
|
|
49
|
+
# It ships packaged (it lives under `lib/`, which the gemspec includes);
|
|
50
|
+
# the spec fixtures deliberately do not, so nothing here may be
|
|
51
|
+
# load-bearing at runtime.
|
|
52
|
+
#
|
|
53
|
+
# Since the SPGD-867 cutover nothing VALIDATES against this copy — every
|
|
54
|
+
# verdict comes from the `validate-intent` binary's own compiled-in
|
|
55
|
+
# schema. It survives because {ValidatorBackend}'s schema-contract check
|
|
56
|
+
# digests it at runtime and refuses a binary that would enforce different
|
|
57
|
+
# bytes: the two halves of the seam must keep meeting somewhere, and this
|
|
58
|
+
# file is where the gem's half lives.
|
|
59
|
+
SCHEMA_PATH = File.expand_path("rspec/schemas/open-test-intent.v1.json", __dir__).freeze
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
require_relative "rspec/finding"
|
|
64
|
+
require_relative "rspec/annotation_scanner"
|
|
65
|
+
require_relative "rspec/payload_normalizer"
|
|
66
|
+
require_relative "rspec/scanner"
|
|
67
|
+
require_relative "rspec/file_selector"
|
|
68
|
+
require_relative "rspec/linter"
|
|
69
|
+
require_relative "rspec/json_reporter"
|
|
70
|
+
require_relative "rspec/validator_backend"
|
|
71
|
+
require_relative "rspec/cli"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# The version of the `specguard-ruby` gem — one number for every adapter the
|
|
4
|
+
# gem ships. It lived in `SpecGuard::RSpec::VERSION` while the gem itself was
|
|
5
|
+
# `specguard-rspec` and rspec was the only adapter; the rename to a
|
|
6
|
+
# language-scoped client (mirroring `specguard-ts` and `specguard-go`) made a
|
|
7
|
+
# framework-scoped version constant a lie, so the number moved here and the
|
|
8
|
+
# rspec module now points at it for back-compat.
|
|
9
|
+
module SpecGuard
|
|
10
|
+
VERSION = "0.3.1"
|
|
11
|
+
end
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Bump the patch version, commit, push to main, and tag vX.Y.Z on what landed.
|
|
3
|
+
#
|
|
4
|
+
# Mirrors yatfa's script/bump-version.sh, adapted for a gem:
|
|
5
|
+
# - the version source is lib/specguard/version.rb (the gemspec reads
|
|
6
|
+
# SpecGuard::VERSION from it; lib/specguard/rspec/version.rb is a back-compat
|
|
7
|
+
# alias that follows it), not a VERSION file
|
|
8
|
+
# - release is tag-based like warden's: the GitHub Release and the pushed gem
|
|
9
|
+
# both correspond to vX.Y.Z. There is no `prod` deploy-mirror branch —
|
|
10
|
+
# nothing here is deployed to a cluster.
|
|
11
|
+
#
|
|
12
|
+
# Strategy carried over verbatim: must be on main with a clean tree, patch-only
|
|
13
|
+
# bump, commit "bump: X -> Y", pull --rebase + push with up to 3 retries.
|
|
14
|
+
#
|
|
15
|
+
# When run inside GitHub Actions (GITHUB_OUTPUT set), exports `version` and
|
|
16
|
+
# `tag` to the step's job outputs. Locally it just prints the new version.
|
|
17
|
+
set -e
|
|
18
|
+
|
|
19
|
+
VERSION_FILE="lib/specguard/version.rb"
|
|
20
|
+
|
|
21
|
+
# Ensure we're on the main branch
|
|
22
|
+
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
|
23
|
+
if [ "$CURRENT_BRANCH" != "main" ]; then
|
|
24
|
+
echo "Error: Not on main branch. Current branch: $CURRENT_BRANCH"
|
|
25
|
+
exit 1
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
# Check that no TRACKED file has uncommitted changes. Untracked files are not
|
|
29
|
+
# a reason to refuse: this runs in CI right after the suite, where
|
|
30
|
+
# ruby/setup-ruby's bundler-cache rewrites Gemfile.lock (untracked here, see
|
|
31
|
+
# .gitignore) and the suite itself may leave artefacts behind. What must be
|
|
32
|
+
# clean is the content the bump is about to commit on top of.
|
|
33
|
+
if [ -n "$(git status --porcelain --untracked-files=no)" ]; then
|
|
34
|
+
echo "Error: tracked files have uncommitted changes:"
|
|
35
|
+
git status --porcelain --untracked-files=no
|
|
36
|
+
exit 1
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
if [ ! -f "$VERSION_FILE" ]; then
|
|
40
|
+
echo "Error: $VERSION_FILE not found"
|
|
41
|
+
exit 1
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
CURRENT_VERSION=$(ruby -e 'require "./lib/specguard/rspec/version"; print SpecGuard::RSpec::VERSION')
|
|
45
|
+
echo "Current version: $CURRENT_VERSION"
|
|
46
|
+
|
|
47
|
+
# Bump patch version (0.2.0 -> 0.2.1)
|
|
48
|
+
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION"
|
|
49
|
+
NEW_VERSION="$MAJOR.$MINOR.$((PATCH + 1))"
|
|
50
|
+
echo "New version: $NEW_VERSION"
|
|
51
|
+
|
|
52
|
+
# A published gem version is immutable — RubyGems refuses a re-push and yanking
|
|
53
|
+
# does not free the number. Refuse locally rather than discovering it after a
|
|
54
|
+
# build, and refuse a reused tag for the same reason the release does.
|
|
55
|
+
if git rev-parse -q --verify "refs/tags/v$NEW_VERSION" >/dev/null || \
|
|
56
|
+
git ls-remote --exit-code --tags origin "refs/tags/v$NEW_VERSION" >/dev/null 2>&1; then
|
|
57
|
+
echo "Error: tag v$NEW_VERSION already exists — the version file and the tags disagree"
|
|
58
|
+
exit 1
|
|
59
|
+
fi
|
|
60
|
+
if command -v curl >/dev/null 2>&1; then
|
|
61
|
+
if curl -fsS --max-time 10 "https://rubygems.org/api/v1/versions/specguard-rspec.json" 2>/dev/null \
|
|
62
|
+
| grep -q "\"number\":\"$NEW_VERSION\""; then
|
|
63
|
+
echo "Error: specguard-rspec $NEW_VERSION is already published on RubyGems"
|
|
64
|
+
exit 1
|
|
65
|
+
fi
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# Rewrite the VERSION constant in place. A targeted sed on the one line, so the
|
|
69
|
+
# surrounding file (frozen_string_literal, module nesting) is untouched.
|
|
70
|
+
sed -i.bak -E "s/(VERSION = \")[0-9]+\.[0-9]+\.[0-9]+(\")/\1$NEW_VERSION\2/" "$VERSION_FILE"
|
|
71
|
+
rm -f "$VERSION_FILE.bak"
|
|
72
|
+
WROTE=$(ruby -e 'load "./lib/specguard/rspec/version.rb"; print SpecGuard::RSpec::VERSION')
|
|
73
|
+
if [ "$WROTE" != "$NEW_VERSION" ]; then
|
|
74
|
+
echo "Error: $VERSION_FILE still reports $WROTE after the rewrite"
|
|
75
|
+
exit 1
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
# Create commit
|
|
79
|
+
git add "$VERSION_FILE"
|
|
80
|
+
git commit -m "bump: $CURRENT_VERSION -> $NEW_VERSION"
|
|
81
|
+
|
|
82
|
+
# Pull with rebase to incorporate any commits that landed on main since checkout,
|
|
83
|
+
# then push the branch and the tag. Retry up to 3 times to handle concurrent pushes.
|
|
84
|
+
#
|
|
85
|
+
# The tag is created INSIDE the loop, after `git push origin main` succeeds, and
|
|
86
|
+
# never before the rebase. `git pull --rebase` replays the bump commit onto the
|
|
87
|
+
# advanced main and gives it a new SHA; tags do not follow a rebase, so a tag
|
|
88
|
+
# created beforehand would name an abandoned object that is not an ancestor of
|
|
89
|
+
# main — while the workflow builds the gem from the post-rebase tree. A published
|
|
90
|
+
# gem version is immutable, so that divergence would be permanent.
|
|
91
|
+
#
|
|
92
|
+
# `-f` makes a retry idempotent when an earlier attempt created the local tag but
|
|
93
|
+
# failed to push it. It cannot clobber a published tag: the tag-reuse guard above
|
|
94
|
+
# already refused that case before any work started.
|
|
95
|
+
MAX_RETRIES=3
|
|
96
|
+
RETRY_COUNT=0
|
|
97
|
+
PUSH_SUCCESS=false
|
|
98
|
+
|
|
99
|
+
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
|
|
100
|
+
if git pull --rebase origin main; then
|
|
101
|
+
# HEAD is now the rebased bump commit — the object the tag must name.
|
|
102
|
+
if git push origin main; then
|
|
103
|
+
git tag -f "v$NEW_VERSION" HEAD
|
|
104
|
+
if git push origin "v$NEW_VERSION"; then
|
|
105
|
+
PUSH_SUCCESS=true
|
|
106
|
+
break
|
|
107
|
+
fi
|
|
108
|
+
fi
|
|
109
|
+
fi
|
|
110
|
+
RETRY_COUNT=$((RETRY_COUNT + 1))
|
|
111
|
+
if [ $RETRY_COUNT -lt $MAX_RETRIES ]; then
|
|
112
|
+
echo "Push failed, retrying ($RETRY_COUNT/$MAX_RETRIES)..."
|
|
113
|
+
sleep 5
|
|
114
|
+
fi
|
|
115
|
+
done
|
|
116
|
+
|
|
117
|
+
if [ "$PUSH_SUCCESS" = false ]; then
|
|
118
|
+
echo "Error: Failed to push version bump after $MAX_RETRIES attempts"
|
|
119
|
+
exit 1
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
echo "Version bumped to $NEW_VERSION and pushed to main with tag v$NEW_VERSION"
|
|
123
|
+
|
|
124
|
+
# Export to GitHub Actions job outputs when run in CI
|
|
125
|
+
if [ -n "$GITHUB_OUTPUT" ]; then
|
|
126
|
+
echo "version=$NEW_VERSION" >> "$GITHUB_OUTPUT"
|
|
127
|
+
echo "tag=v$NEW_VERSION" >> "$GITHUB_OUTPUT"
|
|
128
|
+
fi
|
metadata
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: specguard-ruby
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.3.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- specguard Agent
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-08-29 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: json
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '2.21'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '2.21'
|
|
27
|
+
description: SpecGuard's Ruby client — every Ruby test framework, one gem. Ships an
|
|
28
|
+
additive RSpec formatter and a Minitest reporter that post test-run telemetry to
|
|
29
|
+
SpecGuard's ingest endpoint, a CLI linter (specguard-lint) that validates OpenTestIntent
|
|
30
|
+
@intent annotations in *_spec.rb files, and a replayer (specguard-ingest) that re-delivers
|
|
31
|
+
a run saved when the endpoint could not be reached. Telemetry never blocks CI; the
|
|
32
|
+
linter blocks only on malformed annotations.
|
|
33
|
+
email:
|
|
34
|
+
- specguard-agent@noreply.yatfa.com
|
|
35
|
+
executables:
|
|
36
|
+
- specguard-lint
|
|
37
|
+
- specguard-ingest
|
|
38
|
+
extensions: []
|
|
39
|
+
extra_rdoc_files: []
|
|
40
|
+
files:
|
|
41
|
+
- ".github/workflows/ci.yml"
|
|
42
|
+
- ".github/workflows/release.yml"
|
|
43
|
+
- LICENSE
|
|
44
|
+
- README.md
|
|
45
|
+
- Rakefile
|
|
46
|
+
- assets/built-with-yatfa.png
|
|
47
|
+
- bin/specguard-ingest
|
|
48
|
+
- bin/specguard-lint
|
|
49
|
+
- lib/minitest/specguard_plugin.rb
|
|
50
|
+
- lib/specguard/minitest/reporter.rb
|
|
51
|
+
- lib/specguard/rspec.rb
|
|
52
|
+
- lib/specguard/rspec/annotation_lookup.rb
|
|
53
|
+
- lib/specguard/rspec/annotation_scanner.rb
|
|
54
|
+
- lib/specguard/rspec/cli.rb
|
|
55
|
+
- lib/specguard/rspec/configuration.rb
|
|
56
|
+
- lib/specguard/rspec/file_selector.rb
|
|
57
|
+
- lib/specguard/rspec/finding.rb
|
|
58
|
+
- lib/specguard/rspec/formatter.rb
|
|
59
|
+
- lib/specguard/rspec/ingest_cli.rb
|
|
60
|
+
- lib/specguard/rspec/ingest_reporter.rb
|
|
61
|
+
- lib/specguard/rspec/json_reporter.rb
|
|
62
|
+
- lib/specguard/rspec/linter.rb
|
|
63
|
+
- lib/specguard/rspec/payload_normalizer.rb
|
|
64
|
+
- lib/specguard/rspec/scanner.rb
|
|
65
|
+
- lib/specguard/rspec/schemas/open-test-intent.v1.json
|
|
66
|
+
- lib/specguard/rspec/transport.rb
|
|
67
|
+
- lib/specguard/rspec/validator_backend.rb
|
|
68
|
+
- lib/specguard/rspec/version.rb
|
|
69
|
+
- lib/specguard/version.rb
|
|
70
|
+
- script/bump-version.sh
|
|
71
|
+
homepage: https://github.com/yatfa-ai/specguard-ruby
|
|
72
|
+
licenses:
|
|
73
|
+
- MIT
|
|
74
|
+
metadata:
|
|
75
|
+
homepage_uri: https://github.com/yatfa-ai/specguard-ruby
|
|
76
|
+
bug_tracker_uri: https://github.com/yatfa-ai/specguard-ruby/issues
|
|
77
|
+
post_install_message:
|
|
78
|
+
rdoc_options: []
|
|
79
|
+
require_paths:
|
|
80
|
+
- lib
|
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
82
|
+
requirements:
|
|
83
|
+
- - ">="
|
|
84
|
+
- !ruby/object:Gem::Version
|
|
85
|
+
version: 3.2.0
|
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
87
|
+
requirements:
|
|
88
|
+
- - ">="
|
|
89
|
+
- !ruby/object:Gem::Version
|
|
90
|
+
version: '0'
|
|
91
|
+
requirements: []
|
|
92
|
+
rubygems_version: 3.4.19
|
|
93
|
+
signing_key:
|
|
94
|
+
specification_version: 4
|
|
95
|
+
summary: RSpec formatter, Minitest reporter and @intent annotation linter for SpecGuard.
|
|
96
|
+
test_files: []
|