Skip to content

Honour every --report pair, and stop the action dying silently - #20

Merged
SebHenn merged 1 commit into
mainfrom
fix/19-report-pairs
Aug 21, 2026
Merged

Honour every --report pair, and stop the action dying silently#20
SebHenn merged 1 commit into
mainfrom
fix/19-report-pairs

Conversation

@SebHenn

@SebHenn SebHenn commented Aug 21, 2026

Copy link
Copy Markdown
Owner

Fixes #19 — two bugs with one visible symptom: the step whose whole job is to explain a failure went red without explaining itself.

CLI

--report / --report-out were single-valued, so a second pair silently replaced the first: exit 0, empty stderr, and a file the caller explicitly asked for that was never written.

Both options now repeat and are paired by position, so one analysis produces a markdown report for humans and a SARIF for Code Scanning:

cifail analyze build.log \
  --report markdown --report-out cifail.md \
  --report sarif    --report-out cifail.sarif

Spectre hands each option over as its own list, so the interleaving between the two is lost — which makes exactly two shapes readable: one report with no destination (it takes over stdout, unchanged), or N reports with N destinations. Everything else is a usage error (exit 2) checked before any input is read:

Command line Result
more --report than --report-out give each report its own --report-out
--report-out with no --report --report-out needs a --report to write
two reports aimed at one path the second would overwrite the first

Guessing is how a requested output disappears unnoticed, which is the whole bug.

Action

The runner invokes a composite step as bash --noprofile --norc -e -o pipefail {0}. The script's set -uo pipefail reads like a full declaration of shell options but does not clear that inherited errexit — and every [ -n "$X" ] && arr+=(...) returns 1 when X is empty. A job with no shared database configured aborted at the very first one, before code=$?, before cifail's stderr was echoed, and before the fail: input could apply, which made fail: false inoperative. cifail's own documented exit 1 ("nothing matched") would have done the same thing.

Now: explicit set +e, if blocks instead of &&, and a ::warning:: when the markdown report is absent — ghcr.io/sebhenn/cifail:latest only moves on a release tag, so a pinned older image should degrade to raw JSON with an explanation rather than a blank step.

Tests

  • MultipleReportsTests — both pairs written, order-independent, --json still owns stdout, single report still takes over stdout, and each rejected shape.
  • docker-smoke now extracts action.yml's step script and runs it the way the runner does (bash -e -o pipefail) against the image built from this commit. Neither half of this bug was observable from the CLI alone.

🤖 Generated with Claude Code

Two bugs, one visible symptom: the step whose whole job is to explain a
failure went red without explaining itself (#19).

CLI. --report/--report-out were single-valued, so a second pair silently
replaced the first — exit 0, empty stderr, and a file the caller asked
for that was never written. Both options now repeat and are paired by
position, so one analysis produces a markdown report for humans and a
SARIF for Code Scanning. Spectre hands each option over as its own list,
so the interleaving is lost and only two shapes are readable: one report
with no destination (it takes over stdout, as before), or N reports with
N destinations. Everything else — more reports than destinations, a
--report-out with no --report, two reports aimed at one path — is a usage
error checked before any input is read, because guessing is how a
requested output disappears unnoticed.

Action. The runner invokes a composite step as `bash --noprofile --norc
-e -o pipefail {0}`, and `set -uo pipefail` reads like a full declaration
of shell options but does not clear that inherited errexit. Every
`[ -n "$X" ] && arr+=(...)` returns 1 when X is empty, so a job with no
shared database configured aborted at the first one — before `code=$?`,
before cifail's stderr was echoed, and before the `fail:` input could
apply, which made `fail: false` inoperative. Now `set +e` explicitly,
`if` blocks instead of `&&`, and a ::warning:: when the markdown report
is absent: :latest only moves on a release tag, so a pinned older image
should degrade to raw JSON with an explanation, not a blank step.

Tests: MultipleReportsTests covers the pairs and each rejected shape.
docker-smoke now runs the action's own step script the way the runner
does, against the image built from the commit — neither half of this was
observable from the CLI alone.

Fixes #19

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@SebHenn
SebHenn merged commit 2a25628 into main Aug 21, 2026
4 checks passed
@SebHenn
SebHenn deleted the fix/19-report-pairs branch August 21, 2026 17:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Only the last --report/--report-out pair is honoured, silently — and the shipped action passes two

1 participant