Chapter 16Lesson 01~95 minutes

Non-Interactive Shell Behavior in CI Runners

A CI runner is not your terminal. Reliable pipeline scripts assume a non-interactive shell, explicit environment, controlled working directory, ephemeral local state, and success determined by process exit status.

IntermediateCI/CD automationHands-on lab

Learning objectives

By the end of this lesson

  • Explain interactive versus non-interactive CI shell behavior.
  • Avoid dependencies on startup files and terminal prompts.
  • Control working directory, pagers, and editors.
  • Design for ephemeral runners and cancellation.
  • Use exit status and logs as the job interface.

1. CI shells are not interactive terminals

A CI runner launches commands in a controlled non-interactive environment. There may be no terminal, no prompt, no user profile, no remembered working directory from your laptop, and no opportunity for someone to answer a question.

CI shell execution model
flowchart TD
  Y["pipeline definition"] --> R["CI runner"]
  R --> S["non-interactive shell"]
  S --> C["script / commands"]
  C --> E["exit status + logs + artifacts"]
Core rule

Any command that may stop and wait for input is a production risk in CI.

2. Shell startup files may not run

printf 'shell=%s\n' "${SHELL:-unknown}"
printf 'bash_version=%s\n' "${BASH_VERSION:-not-bash}"
printf 'home=%s\n' "${HOME:-unset}"
printf 'path=%s\n' "$PATH"

Do not rely on aliases, functions, PATH additions, language-version managers, or exports from your interactive ~/.bashrc. Configure dependencies explicitly in the job.

3. Know which shell is actually executing the step

if [[ -z ${BASH_VERSION:-} ]]; then
  printf 'this script requires Bash\n' >&2
  exit 69
fi

printf 'running Bash %s\n' "$BASH_VERSION"

A pipeline may default to sh, PowerShell, cmd.exe, or another shell depending on runner and platform. Bash-specific scripts should use a Bash shebang or invoke Bash deliberately.

4. Commands that require a TTY often fail or hang

# Prefer non-interactive flags:
sudo -n true

# For tools with prompts, use their documented non-interactive mode.
some_cli --non-interactive

Interactive password prompts, confirmation menus, pagers, editors, and credential dialogs are unsuitable unless the CI platform explicitly provides a supported mechanism.

5. Disable pagers and editors

export PAGER=cat
export GIT_PAGER=cat
export GIT_EDITOR=:
export SYSTEMD_PAGER=cat

Tools that open a pager or editor can block unattended jobs. Prefer tool-specific flags such as --no-pager where available.

6. Working directory is part of the contract

script_dir=$(
  cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &&
  pwd -P
) || exit 1

repo_root=$(
  cd -- "$script_dir/../.." &&
  pwd -P
) || exit 1

cd -- "$repo_root" || exit 1

Do not assume the job starts in a repository root unless the pipeline explicitly guarantees it.

7. Runner filesystems may be ephemeral

Many CI jobs execute on short-lived machines or containers. Files created during one job may disappear when that job ends unless you explicitly persist them as artifacts, cache entries, or platform-supported state.

No hidden continuity

Treat every job as a fresh process boundary unless the CI system explicitly documents shared state.

8. Runners can cancel jobs

cleanup() {
  local status=$?
  rm -rf -- "${tmpdir:-}"
  exit "$status"
}
trap cleanup EXIT TERM INT

Cancellation may send signals before the runner forcefully terminates the job. Cleanup should be useful but never assumed to be guaranteed.

9. stdout and stderr become the operator interface

printf 'artifact_id=%s\n' "$artifact_id"
printf 'level=INFO event=build_started\n' >&2

CI logs are often the only forensic record after an ephemeral runner disappears. Keep result data and diagnostics intentionally separated.

10. Exit status is how a job communicates success

if run_tests; then
  printf 'tests=passed\n'
else
  status=$?
  printf 'tests=failed status=%d\n' "$status" >&2
  exit "$status"
fi

A command that prints an error but exits 0 can make the whole job appear successful. Conversely, a command whose non-zero status is expected must be handled explicitly.

11. Hands-on lab: simulate a CI-like shell

mkdir -p "$HOME/devops-academy/bash/chapter16/lesson01"
cd "$HOME/devops-academy/bash/chapter16/lesson01"

cat > ci-env-check.sh <<'EOF'
#!/usr/bin/env bash
set -u
set -o pipefail

[[ -n ${BASH_VERSION:-} ]] || {
  printf 'Bash required\n' >&2
  exit 69
}

export PAGER=cat
export GIT_PAGER=cat
export GIT_EDITOR=:

printf 'interactive=%s\n' \
  "$([[ $- == *i* ]] && printf yes || printf no)"
printf 'bash=%s\n' "$BASH_VERSION"
printf 'pwd=%s\n' "$PWD"
printf 'home=%s\n' "${HOME:-unset}"
printf 'ci=%s\n' "${CI:-false}"

if [[ -t 0 ]]; then
  printf 'stdin_tty=yes\n'
else
  printf 'stdin_tty=no\n'
fi
EOF

chmod u+x ci-env-check.sh

env -i \
  HOME="$HOME" \
  PATH="$PATH" \
  CI=true \
  bash --noprofile --norc ./ci-env-check.sh </dev/null

Verification checklist

12. Knowledge check

Question 1. Why should CI scripts avoid interactive commands?

Question 2. Can a Bash script assume ~/.bashrc ran in CI?

Question 3. What makes a job fail at the pipeline level?

Question 4. Why should CI jobs treat local files as ephemeral?

13. Summary

CI shells are non-interactive, frequently ephemeral, and controlled by exit status. Make the shell, working directory, dependencies, pager/editor behavior, cleanup, and state persistence explicit instead of inheriting assumptions from an interactive workstation.

14. Further reading

  • GNU Bash Reference Manual — invocation and startup files.
  • GNU Bash Reference Manual — interactive versus non-interactive shells.
  • POSIX shell execution and exit status concepts.
  • CI runner documentation for the platform used by your project.
Next lesson

Environment Variables, Secrets, and Masked Output

Continue Chapter 16 by making Bash behavior inside CI/CD pipelines more explicit and portable.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.