specguard-ruby 0.3.16 → 0.3.17

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1bb92235dbd56d9c1d56455180a6639cbfbd021d6f7aed82309d4b6a8f906c70
4
- data.tar.gz: 5663ad64b127fdd194f95ba45648e98fb69caace1cb2a6835bd518d947ef546e
3
+ metadata.gz: b2ae5e6b5a9750e2317709c9c5b4582d7237385e35a627cf03319087de12ed1f
4
+ data.tar.gz: 67733aa5aa6735a51e1e68097892da247b0c023e3a3bf09533e7f21e5abde790
5
5
  SHA512:
6
- metadata.gz: 54af1220410fe4f5cdbeddc883e401fb8fab98aa88514c6a08c520434ccf4630db4be0dce4da31db279dbb976763d8aef49e61c7e27baaabd7f7e3fede943be0
7
- data.tar.gz: 758c52e3e66f71abedad11741b070e555890eb9e5c597c0da6ff1a71f5ae4fef00edd0f66a68868068d58f7923e12690bfd8a86e74603c45f898b7031e01a362
6
+ metadata.gz: 8aa45d1483899e69fbe71d5f819cc404db8086265fbda3566ab68e550544c9439d19014fb3b440b14a3065a339601015ff542e8f57da16a829685d9858403e0e
7
+ data.tar.gz: 4d0e70b0efe28ee9ae91362d29b4502a7c6ae53073b9857fc4ec295eed1cf588cfb9d5a6c507005944f2319644ec84821123a03a92bc9604a6f12d6bade8fb14
data/README.md CHANGED
@@ -60,7 +60,7 @@ directory names, so `spec/vendor_helpers/` is still yours. A bundled Rails tree
60
60
  (`bundle install --deployment` → `vendor/bundle/`) ships thousands of third-party gem
61
61
  specs; the walk neither reports on them nor fails over their annotations, and the
62
62
  zero-annotation coverage note counts only files you wrote — the same material
63
- `--changed` keeps out via `.gitignore`. When the fence removed files, the
63
+ `--changed` fences out with the same directory list. When the fence removed files, the
64
64
  `checked N spec files` line says how many (`skipping M in dependency or build
65
65
  directories`); a file named explicitly on the command line is always checked.
66
66
 
@@ -76,7 +76,11 @@ paths (scratch directories, vendored code, build output) never enter the
76
76
  selection — and untracked files obey the same scoping as diffed ones, so an
77
77
  untracked spec outside the current directory is counted as outside, not
78
78
  checked. When the untracked leg contributed, the `checked N spec files changed
79
- since <base>` line says `including M untracked`.
79
+ since <base>` line says `including M untracked`. `--changed` applies the same
80
+ dependency/build directory fence to everything it selects: a tracked file is
81
+ never subject to `.gitignore`, so the diff leg needs the fence regardless —
82
+ and when the fence removed files, the selection line says how many
83
+ (`skipping M in dependency or build directories`).
80
84
  In a **shallow** checkout — the default depth-1 `git clone`
81
85
  behind `actions/checkout@v4` — the merge base with the default branch is not
82
86
  in the clone's history, so the derived base is HEAD itself and the selection
@@ -263,15 +263,15 @@ module SpecGuard
263
263
  # "the machine reads the provenance the human does" a property of the
264
264
  # code rather than a promise two copies could drift apart on.
265
265
  #
266
- # The `:all` count names its fence: `FileSelector.select_all` refuses
267
- # dependency/build directories (`file_selector.rb`
268
- # `SKIPPED_DIRECTORIES`), and a fence that actually removed files says
266
+ # The fence count names its fence: both selection modes apply
267
+ # `FileSelector`'s `SKIPPED_DIRECTORIES` (the default walk and
268
+ # `--changed` alike), and a fence that actually removed files says
269
269
  # how many — the narrowing must never be silent. The clause is
270
270
  # count-gated exactly like the untracked one, so a run whose fence
271
271
  # removed nothing prints byte-identically to before the fence existed.
272
272
  def selection_line(selection)
273
273
  untracked = selection.mode == :changed ? selection.stats&.untracked.to_i : 0
274
- skipped = selection.mode == :all ? selection.skipped : 0
274
+ skipped = selection.skipped.to_i
275
275
  "specguard-lint: checked #{selection.count} spec file#{'s' unless selection.count == 1}" \
276
276
  "#{" changed since #{selection.base}" if selection.mode == :changed}" \
277
277
  "#{" under #{Dir.pwd}" if selection.mode == :all}" \
@@ -320,28 +320,40 @@ module SpecGuard
320
320
  "#{stats.changed} file#{'s' unless stats.changed == 1} changed against #{base}, " \
321
321
  "none matching *_spec.rb or *_test.rb"
322
322
  else
323
- changed_excluded_reason(stats, base)
323
+ changed_excluded_reason(stats, base, selection.skipped)
324
324
  end
325
325
  end
326
326
 
327
- # `outside_root` and `unreadable` are independent counters over disjoint
328
- # branches of the same partition (`file_selector.rb`), so both can be
329
- # positive at once. Naming only the first one found is the failure the
327
+ # `outside_root`, `unreadable` and the directory-fence count are
328
+ # independent counters over disjoint branches of the same partition
329
+ # (`file_selector.rb`), so any two of them can be positive at once.
330
+ # Naming only the first one found is the failure the
330
331
  # comment above forbids: the reader does the arithmetic, sees that
331
332
  # `spec_matches - outside_root` files are unaccounted for, and concludes
332
333
  # they were checked. They were not — the selection is empty. So the
333
- # clauses are additive, and a selection emptied by both causes says so.
334
- def changed_excluded_reason(stats, base)
334
+ # clauses are additive, and a selection emptied by several causes says
335
+ # so.
336
+ def changed_excluded_reason(stats, base, skipped)
335
337
  matched = "#{stats.spec_matches} changed spec file#{'s' unless stats.spec_matches == 1} against #{base}"
336
338
 
337
- # Nothing outside the root: `unreadable` is then the only filter left
338
- # that can have emptied the selection, so it needs no count of its own.
339
- return "#{matched} could not be read" unless stats.outside_root.positive?
340
-
341
- reason = "#{matched}, but #{stats.outside_root} #{stats.outside_root == 1 ? 'is' : 'are'} " \
342
- "outside #{Dir.pwd} (--changed selects only files under the current directory)"
343
- reason += " and #{stats.unreadable} could not be read" if stats.unreadable.positive?
344
- reason
339
+ if stats.outside_root.positive?
340
+ reason = "#{matched}, but #{stats.outside_root} #{stats.outside_root == 1 ? 'is' : 'are'} " \
341
+ "outside #{Dir.pwd} (--changed selects only files under the current directory)"
342
+ reason += " and #{stats.unreadable} could not be read" if stats.unreadable.positive?
343
+ reason += " and #{skipped} in dependency or build directories" if skipped.positive?
344
+ reason
345
+ elsif stats.unreadable.positive? && skipped.positive?
346
+ "#{matched} could not be read and #{skipped} in dependency or build directories"
347
+ elsif skipped.positive?
348
+ # Every matching file the diff produced was fenced: the shape
349
+ # {all_fenced_reason} uses, in this mode's vocabulary.
350
+ "#{matched}, all in dependency or build directories"
351
+ else
352
+ # Nothing outside the root and nothing fenced: `unreadable` is then
353
+ # the only filter left that can have emptied the selection, so it
354
+ # needs no count of its own.
355
+ "#{matched} could not be read"
356
+ end
345
357
  end
346
358
 
347
359
  # The FAIL block, in the shape `bin/validate-intent --source` emits: two
@@ -16,13 +16,16 @@ module SpecGuard
16
16
  # diff names paths, not directories, and a changed test file is a test
17
17
  # file wherever the author put it.
18
18
  #
19
- # The default walk also carries a fixed directory fence
20
- # ({SKIPPED_DIRECTORIES}): dependency, build-output, scratch and VCS
21
- # directories are never selected, so a bundled tree's `vendor/bundle/`
22
- # gem specs are neither reported on nor failed over — third-party code
23
- # the user did not write. `--changed` draws the same boundary from
24
- # `.gitignore` (`--exclude-standard`) for free; the walk, which must keep
25
- # working outside a repository, carries it as a name list instead.
19
+ # Both modes carry a fixed directory fence ({SKIPPED_DIRECTORIES}):
20
+ # dependency, build-output, scratch and VCS directories are never
21
+ # selected, so a bundled tree's `vendor/bundle/` gem specs are neither
22
+ # reported on nor failed over — third-party code the user did not write.
23
+ # The walk, which must keep working outside a repository, carries the
24
+ # boundary as a name list; `--changed` applies the same name list to the
25
+ # paths git hands back, because a tracked file is never subject to
26
+ # `.gitignore` and the diff leg therefore has no fence of its own.
27
+ # `--exclude-standard` additionally removes ignored *untracked* paths —
28
+ # it stacks with the name list in `--changed` and is no substitute for it.
26
29
  #
27
30
  # == Why `--changed` is not `git diff --name-only`
28
31
  #
@@ -111,8 +114,12 @@ module SpecGuard
111
114
  # the toplevel so its repo-root-relative output flows through the same
112
115
  # scoping as the diff's (from a subdirectory `ls-files` emits
113
116
  # cwd-relative paths, which would defeat it). `--exclude-standard` is the
114
- # boundary: `.gitignore`d paths — scratch directories, vendored code, build
115
- # output — never enter the selection. The two legs are disjoint by
117
+ # untracked leg's boundary: `.gitignore`d paths — scratch directories,
118
+ # vendored code, build output — never enter through it. It is a second
119
+ # removal, not the selection's only fence: ignored *tracked* paths still
120
+ # arrive on the diff leg (git never applies ignore rules to tracked
121
+ # files), which is why the {SKIPPED_DIRECTORIES} fence applies to the
122
+ # union as a whole. The two legs are disjoint by
116
123
  # construction (an untracked path is never a diff path), so the union
117
124
  # cannot double-count; {Stats.untracked} says how many of the selected
118
125
  # files arrived via the untracked leg, and the empty-reason ladder's
@@ -149,11 +156,14 @@ module SpecGuard
149
156
  # * `tmp` — Rails' scratch directory (caches, pids, sockets).
150
157
  # * `log` — Rails' log directory.
151
158
  #
152
- # `--changed` gets this boundary for free `--exclude-standard` keeps
153
- # `.gitignore`d paths (scratch directories, vendored code, build output)
154
- # out of its untracked leg. The walk has no git to ask, because it must
155
- # keep working outside a repository exactly where `--changed` correctly
156
- # refuses, so it carries the boundary as a name list instead. The list
159
+ # `--changed` applies this same list itself: a tracked file is never
160
+ # subject to `.gitignore`, so `--exclude-standard` which keeps
161
+ # `.gitignore`d paths (scratch directories, vendored code, build
162
+ # output) out of the untracked leg alone cannot fence the diff leg
163
+ # and is an additional removal, never a substitute. The walk has no
164
+ # git to ask, because it must keep working outside a repository exactly
165
+ # where `--changed` correctly refuses, so it carries the boundary as a
166
+ # name list too. The list
157
167
  # is matched against whole directory SEGMENTS of the root-relative path
158
168
  # (see {skipped_directory?}), never as a substring — `spec/vendor_helpers/`
159
169
  # is project code — and never against the absolute path.
@@ -193,16 +203,18 @@ module SpecGuard
193
203
 
194
204
  # What a selection produced, plus enough context to report it honestly.
195
205
  #
196
- # `skipped` is the fence count for the `:all` mode ({SKIPPED_DIRECTORIES}),
197
- # defaulted to 0 so every other construction site is untouched. It
206
+ # `skipped` is the {SKIPPED_DIRECTORIES} fence count, carried by BOTH
207
+ # selection modes `:all`'s walk and `:changed`'s union alike; an
208
+ # `:explicit` selection bypasses the fence and always reads 0 —
209
+ # defaulted to 0 so every construction site is untouched. It
198
210
  # deliberately rides `Selection` rather than a new `Stats` member:
199
211
  # `Stats` is documented as the `--changed` empty-reason ladder ("why an
200
- # empty `--changed` selection is empty") and the fence is neither
201
- # `--changed`-bound nor an emptiness explanation — it is report context
202
- # for a selection that may be perfectly full. `Selection` is what this
203
- # file already uses to carry per-mode context (`base`, `note`, `stats`
204
- # are all `--changed`-only and defaulted the same way), so the count
205
- # follows that precedent instead of widening `Stats`'s contract.
212
+ # empty `--changed` selection is empty") and the fence is not an
213
+ # emptiness explanation — it is report context for a selection that may
214
+ # be perfectly full. `Selection` is what this file already uses to
215
+ # carry per-mode context (`base`, `note`, `stats` are all
216
+ # `--changed`-only and defaulted the same way), so the count follows
217
+ # that precedent instead of widening `Stats`'s contract.
206
218
  Selection = Data.define(:files, :mode, :base, :note, :stats, :skipped) do
207
219
  def initialize(files:, mode:, base: nil, note: nil, stats: nil, skipped: 0)
208
220
  super
@@ -276,20 +288,23 @@ module SpecGuard
276
288
  # consult it (a failed diff, a thin base note).
277
289
  is_shallow = shallow_probe(root)
278
290
 
279
- files, stats = changed_files(resolved, root, is_shallow)
291
+ files, stats, skipped = changed_files(resolved, root, is_shallow)
280
292
 
281
293
  Selection.new(files: files, mode: :changed, base: resolved,
282
- note: base_note(resolved, base_kind, root, is_shallow), stats: stats)
294
+ note: base_note(resolved, base_kind, root, is_shallow), stats: stats,
295
+ skipped: skipped)
283
296
  end
284
297
 
285
298
  # Resolves git's repo-root-relative output into paths relative to `root`,
286
- # dropping (and counting) everything the two scoping rules exclude. The
299
+ # dropping (and counting) everything the scoping rules and the
300
+ # {SKIPPED_DIRECTORIES} fence exclude. The
287
301
  # name set is the union of the diff leg and the untracked leg; each name
288
302
  # is tagged with its leg so {Stats.untracked} can attribute the selected
289
303
  # files the diff never saw. The legs are disjoint by construction (an
290
304
  # untracked path is never a diff path), so the plain union cannot
291
305
  # double-count and needs no dedup.
292
- # @return [[Array<String>, Stats]]
306
+ # @return [[Array<String>, Stats, Integer]] the selected files, the
307
+ # filter stats, and how many files the directory fence removed.
293
308
  def changed_files(base, root, is_shallow)
294
309
  names = diff_names(base, root, is_shallow).map { |name| [name, false] }
295
310
 
@@ -307,6 +322,7 @@ module SpecGuard
307
322
  outside = 0
308
323
  unreadable = 0
309
324
  untracked = 0
325
+ skipped = 0
310
326
  specs.each do |(name, was_untracked)|
311
327
  absolute = prefix + name
312
328
  relative = strip_prefix(absolute, root_prefix)
@@ -314,6 +330,16 @@ module SpecGuard
314
330
  outside += 1
315
331
  elsif !File.file?(absolute)
316
332
  unreadable += 1
333
+ elsif skipped_directory?(relative)
334
+ # The same {SKIPPED_DIRECTORIES} fence {select_all} applies,
335
+ # decided on the root-relative path at the same grain. It sits
336
+ # after the scoping arms so it counts a different exclusion and
337
+ # never perturbs `outside_root`/`unreadable`; because the union
338
+ # is already materialized here, one placement covers BOTH legs —
339
+ # the diff leg (where `.gitignore` cannot act, git never applies
340
+ # ignore rules to tracked files) and the untracked leg on a
341
+ # repository that does not ignore its vendored tree.
342
+ skipped += 1
317
343
  else
318
344
  files << relative
319
345
  untracked += 1 if was_untracked
@@ -322,7 +348,8 @@ module SpecGuard
322
348
 
323
349
  [files.sort,
324
350
  Stats.new(changed: names.length, spec_matches: specs.length,
325
- outside_root: outside, unreadable: unreadable, untracked: untracked)]
351
+ outside_root: outside, unreadable: unreadable, untracked: untracked),
352
+ skipped]
326
353
  end
327
354
 
328
355
  # `--diff-filter=d` drops deleted paths — `git diff --name-only` lists
@@ -7,5 +7,5 @@
7
7
  # framework-scoped version constant a lie, so the number moved here and the
8
8
  # rspec module now points at it for back-compat.
9
9
  module SpecGuard
10
- VERSION = "0.3.16"
10
+ VERSION = "0.3.17"
11
11
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: specguard-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.16
4
+ version: 0.3.17
5
5
  platform: ruby
6
6
  authors:
7
7
  - specguard Agent