Repository navigation
Conversation
Author
|
Local note, not part of this change: the root and the |
CybotTM
force-pushed
the
fix/434-console-fatal-errors
branch
7 times, most recently
from
September 26, 2026 21:30
45aa0a0 to
0b3fe22
Compare
CybotTM
marked this pull request as ready for review
September 26, 2026 21:52
CybotTM
marked this pull request as draft
September 27, 2026 06:55
run-test.sh has no set -e, so its exit code was the exit code of its last diff only. A difference in any other diff was printed but did not fail the job. - Record every failed diff and exit non-zero at the end - Compare the exit code of Fractor with expected-exit-code.txt, 0 when the file is absent; a value that is not a number fails the case - Capture stderr in error-output.txt and compare it exactly with expected-error-output.txt when that file exists; stderr is still printed to the log - Pass the variables in env.txt to Fractor when the file exists - Set COLUMNS=120 and unset FORCE_COLOR, so that the output does not depend on the terminal width or forced colors - Keep editors from trimming the padding in expected-error-output.txt Assisted-by: claude-code:claude-opus-5-5 Agent-Session: https://claude.ai/code/session_016aBEJdgVbFutUUNEwFEMQK Agent-Host: 0493f0 Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
When loading the config or building the container throws, bin/fractor.php printed the message only for --output-format=json. The default console format exited 1 with no output at all, so a config with an unresolvable class import looked like a run that did nothing. The catch block follows Rector's bin/rector.php: - print each message as an error block in console format - report the message of every previous exception, in both formats It differs from Rector where the message would otherwise still be lost: - the console message goes to stderr, where Symfony's Application renders uncaught exceptions too, so redirecting stdout does not hide it - a verbosity below normal is raised to normal after the style is configured, so -q or SHELL_VERBOSITY=-1 cannot suppress the only output of a failed run - with -v and higher, each block also names the exception class, file and line, which the issue missed from -vvv - invalid UTF-8 is replaced with U+FFFD when the messages are collected: Nette's Json::encode threw on it, and the OutputWrapper of symfony/console before 6.4.46, 7.4.19 and 8.1.7 wraps with a pattern that empties such a message - a lone \r is normalized as well as \r\n Add four e2e cases, each with its own config: a missing set class, run with SHELL_VERBOSITY=-1; an exception chain run with -q; an exception chain in JSON format; an exception chain run with -v. Fixes FriendsOfTYPO3#434 Assisted-by: claude-code:claude-opus-5-5 Agent-Session: https://claude.ai/code/session_016aBEJdgVbFutUUNEwFEMQK Agent-Host: 0493f0 Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
CybotTM
force-pushed
the
fix/434-console-fatal-errors
branch
from
September 27, 2026 06:55
0b3fe22 to
03e1824
Compare
CybotTM
marked this pull request as ready for review
September 27, 2026 08:27
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
When the config or the container fails to load,
fractor processnow prints the error on stderr in the default console format, instead of exiting 1 with no output. Both formats report the message of every previous exception too.Fixes #434.
Changes
bin/fractor.phpfollows the catch block of Rector'sbin/rector.php: every message of the exception chain, asfatal_errorsviaJson::encode()in JSON format and as an error block fromSymfonyStyleFactoryin console format. It differs from Rector in the places where the message would otherwise still be lost:Application::run()renders uncaught exceptions togetErrorOutput()as well. On stdout,fractor process … > out.txthid the message again. JSON stays on stdout.SymfonyStyleFactoryconfigured the style. The factory applies-qandSHELL_VERBOSITY=-1, anderror()writes at normal verbosity, so the only output of the failed run was suppressed. Symfony's ownrenderThrowable()writes atVERBOSITY_QUIETfor the same reason. Fractor removed--quietin [!!!][TASK] Drop --quiet cli option #412, but Symfony still applies-qat this point.--silent(symfony/console 7.2+) is raised too, on purpose.-vand higher,--ansiand--no-ansikeep working through the factory.-vand higher, each block also names the exception class, the file (basename, as in Symfony'srenderThrowable()) and the line, for exampleParseError in fractor.php line 3, withn/afor a missing file or line, as Symfony prints it. The issue notes that-vvvchanged nothing, and without the location a parse error in the config gives no hint where it is.json_encode()round-trip withJSON_INVALID_UTF8_SUBSTITUTE, which needs no extension). Onmain,Json::encode()already throws on a malformed message (exit 255, PHP fatal), and the chain adds more messages that can be malformed. In console format, theOutputWrapperof symfony/console wraps with theumodifier, andpreg_replace()returns null on malformed UTF-8, so the block is empty. The guard for that is in 6.4.46, 7.4.19 and 8.1.7, not in 6.4.45, 7.4.18 or 8.0.15.\rbecomes\ntoo, not only\r\nas in Rector. symfony/console normalizes\r\nitself only in recent patch releases (not in 6.4.32, 7.4.4 or 8.0.4, present in 6.4.34), and never a lone\r, which overwrites the block on a terminal.e2e/run-test.sh(first commit, separate so it can be judged on its own): the script has noset -e, so only its last diff decided the exit code, and a new case could not fail CI. Every diff now counts. The script also compares Fractor's exit code (expected-exit-code.txt, default 0, must be a number) and, where the case has anexpected-error-output.txt, stderr, exactly. An optionalenv.txtsets environment variables for one case. The script itself setsCOLUMNS=120and unsetsFORCE_COLORfor every case, because the error block is padded to the terminal width and colors depend on the environment..editorconfigkeeps the padding inexpected-error-output.txt. The six existing cases pass unchanged.Four new e2e cases, each with its own config:
fatal-config: a config that imports a missing set class, run withSHELL_VERBOSITY=-1fatal-config-chained: a config throwing a chain of three exceptions with\r\n, a lone\rand invalid UTF-8, run with-qfatal-config-json: a chain in JSON format, with a/and invalid UTF-8 in the message, and an empty expected stderrfatal-config-verbose: a chain of two exceptions created on different lines, one of them namespaced, run with-vTesting
Local runs, PHP 8.5.10, highest dependency versions: symfony/console v6.4.46 in
e2e/(symplify/rule-doc-generator pins^6.4there). CI has not run yet: both workflow runs wait for maintainer approval.e2e/run-test.sh: exit 0, 10 cases. At the first commit alone (withmain's bin): exit 0, 6 cases.composer style:php:check,analyze:php,rector: exit 0.test:php: OK, 320 tests.style:composer:normalize: exit 0, 13 files already normalized. Locally it fails now and then withCommand "normalize" is not defined, on unmodifiedmainas well (1 of 3 runs there).php -n): both formats print U+FFFD, exit 1.bin/fractor.phpagainst the e2e suite, with an unmutated control run that passes. All 22 are caught: exit 0 instead of 1, only the outer message, only the innermost message, at most two messages, JSON with only the first message,main's bin, stdout instead of stderr,warning()instead oferror(), no line-break normalization,\r\nonly, no UTF-8 substitution, noJSON_INVALID_UTF8_SUBSTITUTE, no verbosity reset, a reset only for-q, an unconditional reset, no location, the first location for every block, the full path instead of the basename, the short class name, a bareSymfonyStyleinstead ofSymfonyStyleFactory, the console block also printed in JSON mode, plainjson_encode()instead ofJson::encode().--ansihas no case of its own: a colored block would tie the fixture to the space padding of symfony/console 6.4, and 8.1 pads it with cursor sequences instead.e2e/cannot install.Assisted by claude-code:claude-opus-5-5 — Session