specguard-ruby 0.3.9 → 0.3.10
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 +10 -1
- data/lib/specguard/rspec/cli.rb +10 -1
- data/lib/specguard/rspec/file_selector.rb +76 -15
- data/lib/specguard/version.rb +1 -1
- 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: c7d1dfb3eb7af542eee8c1202dae06dd0d9c3981f5415d12875037c9a9067944
|
|
4
|
+
data.tar.gz: 9d785e02b70ea9e06b0d0cf55ce31cbb1d67c841ea36fb0b7f3c7217a8f20304
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3d5ae9e309ae02ea33679223f712d04edf7c9e06aab22f011fde61484f2f3c8d2c992b02b792caceeb934b63136734840cdd01d0dbe62619b695dd09c3ceb238
|
|
7
|
+
data.tar.gz: 3d3f373483219a5cdbeecdf639065bd99cb90dd648acf4bd75174ae9eba6adefcfc94a6458577480bb526dc636d88b139a4742cda3c12269a320bd5e7f994a3f
|
data/README.md
CHANGED
|
@@ -58,7 +58,16 @@ bundle exec specguard-lint # one-off audit: every *_spec.rb
|
|
|
58
58
|
the default branch** (`origin/HEAD`, then `origin/main`/`origin/master`, then
|
|
59
59
|
their local names) — never a bare working-tree-vs-index `git diff`, which is
|
|
60
60
|
empty on a clean checkout. `--changed=<base>` overrides the base for pipelines
|
|
61
|
-
that know better.
|
|
61
|
+
that know better. Selection also takes in **untracked** files: a brand-new
|
|
62
|
+
spec that has not been `git add`ed is part of what the branch changed, whether
|
|
63
|
+
or not the change is committed yet. One `git ls-files --others
|
|
64
|
+
--exclude-standard` call per run is unioned with the diff — `.gitignore`d
|
|
65
|
+
paths (scratch directories, vendored code, build output) never enter the
|
|
66
|
+
selection — and untracked files obey the same scoping as diffed ones, so an
|
|
67
|
+
untracked spec outside the current directory is counted as outside, not
|
|
68
|
+
checked. When the untracked leg contributed, the `checked N spec files changed
|
|
69
|
+
since <base>` line says `including M untracked`.
|
|
70
|
+
In a **shallow** checkout — the default depth-1 `git clone`
|
|
62
71
|
behind `actions/checkout@v4` — the merge base with the default branch is not
|
|
63
72
|
in the clone's history, so the derived base is HEAD itself and the selection
|
|
64
73
|
comes up empty: it still exits `0`, but its stderr note (and `--json`
|
data/lib/specguard/rspec/cli.rb
CHANGED
|
@@ -228,14 +228,23 @@ module SpecGuard
|
|
|
228
228
|
# `summary.files`. The warnings are diagnostics about the linter rather
|
|
229
229
|
# than findings, so they stay on stderr exactly as they are — a run that
|
|
230
230
|
# selected nothing is still loud, in both renderers.
|
|
231
|
+
#
|
|
232
|
+
# The `--changed` count names its provenance: files can reach the
|
|
233
|
+
# selection through the untracked leg (`file_selector.rb`), and "changed
|
|
234
|
+
# since <base>" alone over-claims for a file the diff never saw. When
|
|
235
|
+
# the untracked leg contributed, the line says how many — the same
|
|
236
|
+
# `stats.untracked` the selection carries, so the clause can only ever
|
|
237
|
+
# name files this run actually checked.
|
|
231
238
|
def report_selection(selection, json:)
|
|
232
239
|
if selection.empty?
|
|
233
240
|
@stderr.puts "specguard-lint: warning: selected 0 spec files — #{empty_reason(selection)}"
|
|
234
241
|
@stderr.puts "specguard-lint: warning: #{selection.note}" if selection.note
|
|
235
242
|
elsif !json
|
|
243
|
+
untracked = selection.mode == :changed ? selection.stats&.untracked.to_i : 0
|
|
236
244
|
@stdout.puts "specguard-lint: checked #{selection.count} spec file#{'s' unless selection.count == 1}" \
|
|
237
245
|
"#{" changed since #{selection.base}" if selection.mode == :changed}" \
|
|
238
|
-
"#{" under #{Dir.pwd}" if selection.mode == :all}"
|
|
246
|
+
"#{" under #{Dir.pwd}" if selection.mode == :all}" \
|
|
247
|
+
"#{" including #{untracked} untracked" if untracked.positive?}"
|
|
239
248
|
end
|
|
240
249
|
end
|
|
241
250
|
|
|
@@ -87,10 +87,32 @@ module SpecGuard
|
|
|
87
87
|
# quotes and all, which no longer names a file — an accented spec file would
|
|
88
88
|
# be silently dropped and then misreported as "nothing changed".
|
|
89
89
|
#
|
|
90
|
-
#
|
|
91
|
-
#
|
|
92
|
-
#
|
|
93
|
-
#
|
|
90
|
+
# == Untracked files are selected too
|
|
91
|
+
#
|
|
92
|
+
# `git diff` cannot see a file that has never been `git add`ed — but "what
|
|
93
|
+
# this branch changed, whether or not the change is committed yet" covers a
|
|
94
|
+
# never-added file, and a mode that silently skipped it would ride the
|
|
95
|
+
# branch's newest spec past the gate: in the mixed shape every real working
|
|
96
|
+
# tree has (a tracked edit somewhere, the new spec still untracked), the
|
|
97
|
+
# selection was non-empty without the new file, so the loud-empty machinery
|
|
98
|
+
# never fired and the defect shipped behind a checked-count reporting
|
|
99
|
+
# success.
|
|
100
|
+
#
|
|
101
|
+
# So the `--changed` name set is a union: the diff's paths plus ONE
|
|
102
|
+
# `git ls-files --others --exclude-standard -z` call per selection, run at
|
|
103
|
+
# the toplevel so its repo-root-relative output flows through the same
|
|
104
|
+
# scoping as the diff's (from a subdirectory `ls-files` emits
|
|
105
|
+
# cwd-relative paths, which would defeat it). `--exclude-standard` is the
|
|
106
|
+
# boundary: `.gitignore`d paths — scratch directories, vendored code, build
|
|
107
|
+
# output — never enter the selection. The two legs are disjoint by
|
|
108
|
+
# construction (an untracked path is never a diff path), so the union
|
|
109
|
+
# cannot double-count; {Stats.untracked} says how many of the selected
|
|
110
|
+
# files arrived via the untracked leg, and the empty-reason ladder's
|
|
111
|
+
# "nothing changed against <base>" can now only fire when the working tree
|
|
112
|
+
# is genuinely clean. That `ls-files` call is the untracked leg's entire
|
|
113
|
+
# added cost, and it is scoped to this mode — the "zero new git
|
|
114
|
+
# invocations on the full-clone happy path" property SPGD-1035 established
|
|
115
|
+
# belongs to the lazy shallow probe alone, not to `--changed` as a whole.
|
|
94
116
|
module FileSelector
|
|
95
117
|
# `test/**/*_test.rb` rather than `**/*_test.rb`: the `test/` directory
|
|
96
118
|
# is where both Rails and Minitest itself put Minitest files, and
|
|
@@ -112,9 +134,13 @@ module SpecGuard
|
|
|
112
134
|
|
|
113
135
|
# Why an empty `--changed` selection is empty. Every count is a filter
|
|
114
136
|
# this class applied, in the order it applied them, so the CLI can name
|
|
115
|
-
# the real cause instead of guessing at the last one.
|
|
116
|
-
|
|
117
|
-
|
|
137
|
+
# the real cause instead of guessing at the last one. The exception is
|
|
138
|
+
# `untracked`, which filters nothing: it counts the selected files that
|
|
139
|
+
# arrived via the untracked leg, so the report can say where a file the
|
|
140
|
+
# diff never saw came from (an untracked spec outside `root` is counted
|
|
141
|
+
# in `outside_root`, not here — this names files the run checked).
|
|
142
|
+
Stats = Data.define(:changed, :spec_matches, :outside_root, :unreadable, :untracked) do
|
|
143
|
+
def initialize(changed: 0, spec_matches: 0, outside_root: 0, unreadable: 0, untracked: 0)
|
|
118
144
|
super
|
|
119
145
|
end
|
|
120
146
|
end
|
|
@@ -178,20 +204,31 @@ module SpecGuard
|
|
|
178
204
|
end
|
|
179
205
|
|
|
180
206
|
# Resolves git's repo-root-relative output into paths relative to `root`,
|
|
181
|
-
# dropping (and counting) everything the two scoping rules exclude.
|
|
207
|
+
# dropping (and counting) everything the two scoping rules exclude. The
|
|
208
|
+
# name set is the union of the diff leg and the untracked leg; each name
|
|
209
|
+
# is tagged with its leg so {Stats.untracked} can attribute the selected
|
|
210
|
+
# files the diff never saw. The legs are disjoint by construction (an
|
|
211
|
+
# untracked path is never a diff path), so the plain union cannot
|
|
212
|
+
# double-count and needs no dedup.
|
|
182
213
|
# @return [[Array<String>, Stats]]
|
|
183
214
|
def changed_files(base, root, is_shallow)
|
|
184
|
-
names = diff_names(base, root, is_shallow)
|
|
185
|
-
specs = names.select { |name| CHANGED_PATTERNS.any? { |pattern| File.fnmatch?(pattern, name) } }
|
|
215
|
+
names = diff_names(base, root, is_shallow).map { |name| [name, false] }
|
|
186
216
|
|
|
187
217
|
top = toplevel(root)
|
|
218
|
+
names += untracked_names(top.empty? ? root : top).map { |name| [name, true] }
|
|
219
|
+
|
|
220
|
+
specs = names.select { |(name, _)|
|
|
221
|
+
CHANGED_PATTERNS.any? { |pattern| File.fnmatch?(pattern, name) }
|
|
222
|
+
}
|
|
223
|
+
|
|
188
224
|
prefix = directory_prefix(top.empty? ? root : top)
|
|
189
225
|
root_prefix = directory_prefix(real_path(root))
|
|
190
226
|
|
|
191
227
|
files = []
|
|
192
228
|
outside = 0
|
|
193
229
|
unreadable = 0
|
|
194
|
-
|
|
230
|
+
untracked = 0
|
|
231
|
+
specs.each do |(name, was_untracked)|
|
|
195
232
|
absolute = prefix + name
|
|
196
233
|
relative = strip_prefix(absolute, root_prefix)
|
|
197
234
|
if relative.nil?
|
|
@@ -200,12 +237,13 @@ module SpecGuard
|
|
|
200
237
|
unreadable += 1
|
|
201
238
|
else
|
|
202
239
|
files << relative
|
|
240
|
+
untracked += 1 if was_untracked
|
|
203
241
|
end
|
|
204
242
|
end
|
|
205
243
|
|
|
206
244
|
[files.sort,
|
|
207
245
|
Stats.new(changed: names.length, spec_matches: specs.length,
|
|
208
|
-
outside_root: outside, unreadable: unreadable)]
|
|
246
|
+
outside_root: outside, unreadable: unreadable, untracked: untracked)]
|
|
209
247
|
end
|
|
210
248
|
|
|
211
249
|
# `--diff-filter=d` drops deleted paths — `git diff --name-only` lists
|
|
@@ -233,6 +271,26 @@ module SpecGuard
|
|
|
233
271
|
out.split("\0").reject(&:empty?)
|
|
234
272
|
end
|
|
235
273
|
|
|
274
|
+
# The untracked leg of `--changed`: every file git does not track, minus
|
|
275
|
+
# what `--exclude-standard` excludes (`.gitignore`, `.git/info/exclude`,
|
|
276
|
+
# the global excludes file). Run at the toplevel, where `ls-files` emits
|
|
277
|
+
# repo-root-relative paths — from a subdirectory it emits cwd-relative
|
|
278
|
+
# ones, which would break the root-scoping the diff leg's output goes
|
|
279
|
+
# through. `-z` for the same reason as {diff_names}: NUL-separated and
|
|
280
|
+
# never `core.quotePath`-quoted. Without `--directory` git lists the
|
|
281
|
+
# files inside an untracked directory rather than the directory itself,
|
|
282
|
+
# which is what the suffix filter wants. `ls-files` reads the working
|
|
283
|
+
# tree and the index only — no history — so the leg works in a shallow
|
|
284
|
+
# clone where the diff base barely exists. A failure degrades to an
|
|
285
|
+
# empty leg rather than failing the run: the repository was already
|
|
286
|
+
# verified, the tracked diff above remains the primary evidence, and a
|
|
287
|
+
# half-readable working tree must not kill a gate that would otherwise
|
|
288
|
+
# check every tracked spec.
|
|
289
|
+
def untracked_names(chdir)
|
|
290
|
+
out, ok = git(%w[ls-files --others --exclude-standard -z], chdir)
|
|
291
|
+
ok ? out.split("\0").reject(&:empty?) : []
|
|
292
|
+
end
|
|
293
|
+
|
|
236
294
|
# The repository's top level — what `git diff`'s paths are relative to.
|
|
237
295
|
# Empty when git cannot say, in which case the caller falls back to `root`
|
|
238
296
|
# (the top level *is* root for the common case of running from there).
|
|
@@ -308,9 +366,12 @@ module SpecGuard
|
|
|
308
366
|
# Memoized per-run `git rev-parse --is-shallow-repository` probe.
|
|
309
367
|
# SPGD-1035: shallowness is consulted only in the branches where it
|
|
310
368
|
# changes the message (a derived base of HEAD, a failed diff), so the
|
|
311
|
-
#
|
|
312
|
-
# the
|
|
313
|
-
#
|
|
369
|
+
# probe makes zero new git invocations of its own on the full-clone
|
|
370
|
+
# happy path, and the memo caps it at one per run. (That zero is the
|
|
371
|
+
# probe's, not the mode's: the untracked leg adds its one `ls-files`
|
|
372
|
+
# call to every `--changed` selection, whatever the clone's depth.) An
|
|
373
|
+
# unreadable answer (old git without the flag) reads as not shallow,
|
|
374
|
+
# keeping today's messages exactly.
|
|
314
375
|
# @return [Proc] zero-argument; true iff git reports a shallow repository
|
|
315
376
|
def shallow_probe(root)
|
|
316
377
|
cached = nil
|
data/lib/specguard/version.rb
CHANGED
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.10
|
|
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-09-
|
|
11
|
+
date: 2026-09-14 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: json
|