Skip to content

Run CI steps under a strict shell (errexit, pipefail)

This is a principle

A reusable technical claim: something I would want true in any of my work.

Claim. Run CI run: steps under a strict shell - errexit, nounset-where-safe, and crucially pipefail - instead of the platform's lenient default, so any failure in a command or anywhere in a pipe fails the step.

When to apply. Any CI run: step, especially ones that pipe (cmd | tee, cmd | grep) or chain multiple commands.

Why. By default a shell reports only the exit status of the last command in a pipeline. failing-check | tee log.txt exits 0 because tee succeeded, so a red check renders as a green step - a silent false pass, the worst CI failure mode. set -o pipefail propagates the failure of any pipe element; -e (errexit) aborts on the first failing command rather than plowing ahead. Setting this once at the job level makes every step fail honestly by construction, instead of relying on each script author to remember.

Snippet.

jobs:
  governance:
    defaults:
      run:
        shell: bash -elo pipefail {0}   # -e errexit, -l login, -o pipefail

How enforced. A job-level defaults.run.shell, so it applies to every step without per-step boilerplate. Complements Mirror every local guard in CI: mirroring the guards is pointless if a piped guard can fail without failing the build.

The following pages link to this page: