Challenge 3: Spot the Swallowed Exit Code — Possible Solution ==================================================================== THE PROBLEM ------------- run: npm test | tee test-output.log WHY THIS CAN REPORT SUCCESS EVEN WHEN TESTS FAILED -------------------------------------------------------- In a standard shell pipeline (command1 | command2), the EXIT CODE of the whole pipeline is, by default, the exit code of the LAST command in the pipe — not the first one. Here, the last command is tee, and tee's job is simply to copy its input to both stdout and a file (test-output.log) — tee itself almost always succeeds and exits with code 0, REGARDLESS of whether npm test (the first command, piped INTO tee) actually failed with a non-zero exit code of its own. This means: even if npm test genuinely fails (returns exit code 1 or higher because real tests failed), the overall run: step's reported exit code is tee's exit code (0, success) — not npm test's real exit code. GitHub Actions (or any CI system) sees a 0 exit code and marks the step, and therefore the whole job, as PASSED — silently defeating the entire "fail the build on test failure" mechanism this chapter describes as the actual enforcement behind CI, even though the tests genuinely did fail. WHY THIS IS THE EXACT KIND OF ISSUE THE CHAPTER WARNS ABOUT ------------------------------------------------------------------ This is precisely the chapter's own warning about "swallowing/hiding a test command's real exit code" via "piping test output through another command that always exits 0" — tee is a completely ordinary, everyday command developers add for a reasonable-seeming purpose (saving test output to a log file for later inspection), with no obvious intent to break anything — which is exactly why this mistake is so easy to make and so easy to miss in a code review. THE FIX -------- run: | set -o pipefail npm test | tee test-output.log Adding "set -o pipefail" (a bash shell option) changes how the pipeline's overall exit code is determined: instead of always using the LAST command's exit code, it uses the exit code of the FIRST command in the pipe that actually fails (or 0 if none fail). With this option set, if npm test fails, the pipeline's reported exit code correctly reflects that failure, regardless of tee's own (always successful) exit code — restoring the "fail the build on test failure" guarantee while still keeping the convenience of writing test output to a log file.