dev: ignore SIGPIPE errors from truncated output in a more robust way [#2405]

This commit is contained in:
Simon Michael 2025-06-12 18:33:43 -10:00
parent c335f93bcb
commit 0750a27d00

View File

@ -149,7 +149,7 @@ import System.Console.ANSI (Color(..),ColorIntensity(..), ConsoleLayer
import System.Console.Terminal.Size (Window (Window), size) import System.Console.Terminal.Size (Window (Window), size)
import System.Directory (getHomeDirectory, getModificationTime, findExecutable) import System.Directory (getHomeDirectory, getModificationTime, findExecutable)
import System.Environment (getArgs, lookupEnv, setEnv, getProgName) import System.Environment (getArgs, lookupEnv, setEnv, getProgName)
import System.Exit (exitFailure, exitSuccess) import System.Exit (exitFailure)
import System.FilePath (isRelative, (</>)) import System.FilePath (isRelative, (</>))
import "Glob" System.FilePath.Glob (glob) import "Glob" System.FilePath.Glob (glob)
import System.Info (os) import System.Info (os)
@ -287,31 +287,32 @@ exitWithErrorMessage msg = printError msg >> exitFailure
-- it adds (english) text explaining the problem and what to do. -- it adds (english) text explaining the problem and what to do.
-- --
-- Some exceptions this does not handle: -- Some exceptions this does not handle:
-- ExitCode (exitSuccess / exitFailure / exitWith calls) -- ExitCode (exitSuccess/exitFailure/exitWith),
-- and UserInterrupt (control-C). -- UserInterrupt (control-C).
--
-- Also, this ignores SIGPIPE errors, which are usually harmless,
-- caused when our output is truncated in a piped command.
-- --
exitOnError :: IO () -> IO () exitOnError :: IO () -> IO ()
exitOnError = flip catches exitOnError = flip catches [
[
-- Handler (\(e::SomeException) -> error' $ pshow e), -- debug -- Handler (\(e::SomeException) -> error' $ pshow e), -- debug
Handler (\(e::UnicodeException) -> exitUnicode e) Handler (\(e::UnicodeException) -> exitUnicode e)
,Handler (\(e::IOException) -> if ,Handler (\(e::IOException) -> if
| isUnicodeError e -> exitUnicode e | isUnicodeError e -> exitUnicode e
| isBrokenPipeError e -> exitSuccess
| otherwise -> exitOther e) | otherwise -> exitOther e)
,Handler (\(e::ErrorCall) -> exitOther e) ,Handler (\(e::ErrorCall) -> exitOther e)
] ] . ignoreSigPipe
where where
-- After adding the above handler, truncating program output eg by piping into head -- | Ignore SIGPIPE errors.
-- showed "hledger: Error: <stdout>: commitBuffer: resource vanished ( Broken pipe )". -- This is copied from System.Process.Internals in process 1.6.20.0+,
-- As far as I know, this is an IOException and the best we can do is check for that wording -- since that version of process comes only with ghc 9.10.2+.
-- and treat those as non-errors. (Will this mask any real errors ? XXX) ignoreSigPipe :: IO () -> IO ()
isBrokenPipeError :: Exception e => e -> Bool ignoreSigPipe = handle $ \e -> case e of
isBrokenPipeError ex = IOError { ioe_type = ResourceVanished
let msg = map toLower (show ex) in any (`isInfixOf` msg) [ , ioe_errno = Just ioe }
"broken pipe" | Errno ioe == ePIPE -> return ()
] _ -> throwIO e
-- Many decoding failures do not produce a UnicodeException, unfortunately. -- Many decoding failures do not produce a UnicodeException, unfortunately.
-- So this fragile hack detects them from the error message. -- So this fragile hack detects them from the error message.