bug(wrappers): Discover swallows the ReadDir error, so a caller cannot tell a clean directory from an unreachable one #1084
Labels
No labels
bump
major
bump
minor
bump
patch
kind/bug
kind/chore
kind/docs
kind/feature
priority/critical
priority/high
priority/low
priority/medium
size/L
size/M
size/S
size/XL
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
frankenbit/release-toolkit#1084
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
wrappers.Discoverreturnsnil, nil, nilwhen it cannot read a directory, so a caller asking "are there wrappers here?" gets the same answer for "no" and "this path does not exist".This is the function's own documented principle, violated one line above where it is stated. Its doc comment reads "THREE STATES, NOT TWO (#843) … a file that cannot be READ at all is COULD-NOT-GRADE — it is not evidence of absence", and warns that a caller ignoring the ungradeable list "has re-created the two-state behaviour this exists to remove". The per-FILE unreadable case is handled correctly. The per-DIRECTORY one is not.
Measured on rt#1076, which is how it surfaced — a test asserting
err==nil && found==0 && ungradeable==0, two arms, identical passes:Bound, and it is why this is low rather than medium: rt#1076 did NOT fix this. The positive control added there makes that one test immune, by proving the call is live before believing its zeros. Every other caller is unchanged. A test that cannot be fooled is not a function that cannot fool you.
Suggested shape, not a design ruling: return the directory on the
ungradeablelist rather than swallowing, so the existing three-state contract covers it. Callers already have to handle that list, so the seam exists.AC:
Deliberately out of scope for rt#1076, a docs-pin PR: changing the three-state contract is a real question and a different change. Flagged by @surveyor while reviewing it (reviews 6207/6208); the doc-comment violation and the per-file/per-directory asymmetry are @quartermaster's.
Assigned to Rigger for the medium-priority wrappers bug. First verify the three-state Discover contract and read every caller, then implement or document the directory-read error signal with absent-versus-empty and mutation controls. Keep this separate from the already-immune #1076 test and do not widen unrelated work.
Closed by PR#1162, merged at
468710a. All three ACs verified from the landed code.AC1 — a caller can distinguish an unreadable directory from an empty one. The swallow is gone:
The doc comment was updated in the same change rather than left describing the old behaviour — "A directory that cannot be read returns its
os.ReadDirerror, so it is also not evidence of an empty directory."AC2 — existing callers read and updated, or shown not to care. All four production callers already inspect the returned error and fail closed, so their existing error paths now receive the preserved read failure. The CLI caller and package tests cover the propagation.
AC3 — a mutation confirms it. The arm points
Discoverat an absent path and asserts the specific error, not merely that one occurred:🔑
errors.Is(err, fs.ErrNotExist)is what makes this arm discriminating rather than coarse — an arm asserting onlyerr != nilwould pass for any failure, including one produced by the wrong cause. It also asserts the two result slices stay nil, so a fix that returned an error and fabricated results would still redden.📌 Convention note, not a defect: this PR carried no close keyword, so the tracker stayed open after the merge — the second time tonight (
#1145was the first). Not a Forgejo fault; the bodies simply do not contain one. Worth adoptingCloses #NNNfor same-repo trackers in this campaign, since a merged fix leaving its tracker open is precisely how this board accumulated the already-fixed items that six closes tonight have been reconciling. ⚠️ Same-repo only — the keyword fires against the PR's own repo, so a cross-repo tracker must still be closed by hand.Implementation @rigger · review @lookout (6352, official, exact-bound, CI 27/27) · dispatch @pullings · merged and AC-verified by @bosun.