On this page
Keep a command's error output
Every error a Wisent product gives names what failed, why, and how to repair
it. A command whose stderr was thrown away fails with nothing but its exit
status, so the error built from it can only say that something failed.
Every stado quality check of a product that declares its release
(.wisent-release.json), including the pre-push check (--pre-push) and
the check of one revision (--revision), therefore reads the lines each
checked revision adds against the published main (every line when the
checkout has no origin) and refuses a line that throws stderr away without
saying why. One check judges this rule, in every product, before any gate
runs.
| The added line | Example |
|---|---|
sends stderr to /dev/null |
ls /nowhere 2>/dev/null, 2> /dev/null |
| sends both outputs there | &>/dev/null, >/dev/null 2>&1 |
| closes stderr | 2>&- |
| gives a Rust child process a null stderr | .stderr(Stdio::null()) |
Which lines are read
Only lines that run on a host are read. Prose (.md, .mdx) and the
top-level tests, docs and .github directories are not: the same files
the check of a product's reach on other services reads. A test holds these
forms on purpose, to prove the check refuses them.
A line that is a comment as a whole runs no command, so a comment that names
a form, as this page does, is not refused; it is read only as the reason
heading the lines below it. Such a line starts with //, with /* or a *
continuation of it (Rust, Swift, JavaScript, C), or with # (shell, Python,
TOML, YAML), except a Rust attribute (#[, #![) and a script's interpreter
line (#!), which are code. A comment after a command on the same line does
not make the command a comment: ls missing 2>/dev/null # quiet is refused.
Lines a revision does not add are not judged, so an older line is repaired
when its file is next changed for it. On a line it does add, only a discard
the revision adds is judged: one whose command text — the discard and the
command before it, back to the nearest ;, &, |, backtick, quote or
parenthesis — the file holds more often than at the published base. A line
rewritten around a discard its base already held is the base's: a generated
one-line search index that quotes ls /nowhere 2>/dev/null from a page is
rewritten whenever any sentence of that page changes, and a JSON file cannot
carry the comment that would explain it. A discard that is new, or a further
copy of one the base held, is refused on its line.
The refusal
The check refuses as stado.quality.check.discards-unexplained and names
every line as <path>:<line>: <text>, the lines joined by ; :
Error: checking that <revision> of <checkout> says why each stderr it discards is expected: <revision> of <checkout> adds lines that discard stderr without saying why: <N> line(s); observed: probe.sh:2: ls /nowhere 2>/dev/null; …
fix: report the failure such a line hides (capture its stderr and name the command), or, where that failure is the expected answer, name it and why in a `# stderr: <why>` comment (`// stderr: <why>` in Rust) on that line or heading its run of discarding lines
cause: stado.quality.check.discards-unexplained; `stado errors show stado.quality.check` lists every way it fails
How to repair it
Keep the command's stderr and carry its words into the error the program
gives: in a shell program a host runs, capture it (out=$(cmd 2>&1)) and
print it on the line that reports the failure; in a program that starts a
child, read its stderr and put it in the error.
A command whose failure is the expected answer, such as launchctl print of
a unit that is not loaded or a command -v probe whose only answer is its
exit status, names that failure and why in a comment of the language the
line is written in: # stderr: <why> (// stderr: <why> in Rust), on the
same line or heading the run of discarding lines and comments it covers.
stderr discarded: <why> is the same note. The words alone, without a
reason after them, do not pass the line.
# stderr: command -v answers by its status alone
command -v rsync >/dev/null 2>&1 || echo "rsync is not installed"
A failure let pass
The same check holds a shell script (a .sh, .bash or .zsh file, or one
whose first line is a #! naming a shell) to the rule for a failure it lets
pass whole: || true, || : and set +e go on as if the command had
answered. A Swift source is held to it for try?, which reads a failure as
nil, an empty catch {}, which drops it, and .localizedDescription, which
keeps only its words. Such a line names the failure it lets pass and why in
a # failure: <why> comment (// failure: <why> in Swift) on the line or
heading its run, or the revision is refused as
stado.quality.check.failures-dropped-unexplained. Rust is not read for
these: its forms are counted by clippy (stado-rs/clippy.toml). A revision
with both kinds is refused once, the dropped failures said after the
discards.
Source: this website