Published on

TIL: a pipe into tail returns tail exit code, so a failed push looks like success

Authors

A script pushed a branch and trimmed the noisy output:

git push origin my-branch | tail -3
echo "pushed"

The push was rejected. The script still printed "pushed", because a pipeline's exit status is the status of its last command, and tail succeeded.

You can see it in one line:

false | tail -1; echo $?
# 0

set -o pipefail
false | tail -1; echo $?
# 1

I ran both in bash. Without pipefail the failure is gone. With it, the pipeline reports the first non-zero status.

Two ways to fix it:

  • Put set -o pipefail at the top of the script (and set -e if you want it to stop).
  • Do not pipe a command whose success you need to check. Run git push on its own and print the tail of its log separately.

I hit this while debugging an automation that kept reporting stale state after a push that had failed. The command was never the problem. The pipe hid it.