Repository navigation
Print the post-analysis summary to stdout in CI when the error output before it went to stdout - #6740
phpstan-bot wants to merge 1 commit into
Conversation
… before it went to stdout - CI runners (GitHub Actions etc.) read stdout and stderr through separate pipes and merge them non-deterministically, so stderr lines written after the stdout error table (diagnose output, "Elapsed time", "Peak memory", result cache tip, "Result is incomplete") could show up in the middle of the table. - AnalyseCommand now picks a summary output: stdout when CI is detected and the human-readable `table` error format is used (or a baseline is being generated, whose messages also go to stdout), stderr otherwise. Machine-readable formats (json, checkstyle, ...) keep stdout clean. - InceptionResult::handleReturn() accepts an optional output for the elapsed time and peak memory lines. - Also applied to the --generate-baseline path, which prints its result to stdout followed by the summary.
staabm
left a comment
There was a problem hiding this comment.
In which commit did this regress?
|
It isn't a regression. The summary has gone to stderr, while the error table goes to stdout, ever since each piece of it was added:
I looked for later changes with So the order was never guaranteed when stdout and stderr are read separately. It only looks right in a terminal, where both streams write to the same TTY in order. GitHub Actions reads them through separate pipes and merges them, so the summary can end up in the middle of the table. That's why the linked issue is a feature request rather than a bug report, and why this PR changes behaviour in CI instead of reverting an earlier commit. I didn't change any code, so there's nothing new to commit or push. |
|
I wouldn't rush to fix this issue in a nasty way. In reality it's a bug to be reported to GitHub. |
Summary
In CI, the performance summary ("Elapsed time", "Peak memory") and the
--debugdiagnose output sometimes landed in the middle of the error table. PHPStan writes the error table to stdout and the summary to stderr. CI runners like GitHub Actions read the two streams through separate pipes and merge them in no fixed order, so flushing in PHP can't fix it (Symfony'sStreamOutputalready callsfflush()after every write).With this change, PHPStan writes everything that follows human-readable stdout output to stdout as well, but only when a CI is detected. The order is then decided by a single stream.
Changes
src/Command/AnalyseCommand.php: added a$summaryOutputthat is stdout whenCiDetectordetects a CI and the error format istable(or--generate-baselineis used), and stderr otherwise. It is used for:-vvv/--debug)src/Command/InceptionResult.php:handleReturn()takes an optional?Output $summaryOutput(defaults to the error output).--generate-baselinepath. Its success, warning and error messages go to stdout and the summary used to follow on stderr.json,checkstyle,junit,gitlab,raw, …) still write the summary to stderr so their stdout stays parseable. Outside CI, nothing changes.Root cause
Output that should appear after stdout content was written to a different stream (stderr). When the consumer reads the two streams independently, as CI runners do, there is no ordering guarantee between them.
Test
Added
AnalyseCommandTestcases that setGITHUB_ACTIONS=trueand capture stderr separately:tableformat: diagnose output, elapsed time and peak memory are on stdout, not stderr (fails without the fix)jsonformat: they stay on stderr--generate-baseline: the elapsed time follows the baseline message on stdout (fails without the fix)Fixes phpstan/phpstan#15455
🤖 Generated with Claude Code