I'm hardening a small deploy script for a Linux VPS and I'm running into the classic "set -euo pipefail" surprises. The script pulls a release, runs a build, and restarts a service. I want it to fail fast, but a few things behave differently than I expect.
Here's a trimmed version:
#!/usr/bin/env bash
set -euo pipefail
RELEASE_DIR="/srv/app/releases/$(date +%Y%m%d%H%M%S)"
mkdir -p "$RELEASE_DIR"
# 1) grep returning 1 (no match) kills the script here
COUNT=$(grep -c "ERROR" /var/log/app/build.log | head -n 1)
# 2) this function fails inside an if, but the script keeps going
deploy_step() {
cp -r ./build/. "$RELEASE_DIR"
false # simulated failure
echo "should not print"
}
if deploy_step; then
echo "deploy ok"
fi
systemctl restart app.service
What I'm trying to understand:
1. For case 1, is "|| true" the right fix, or is there a cleaner pattern for commands where a non-zero exit is a normal result (grep, diff, test)?
2. For case 2, why doesn't set -e stop inside deploy_step when it's called from an if? What's the recommended way to make functions fail properly in that situation?
3. Would you add a trap on ERR to roll back to the previous release, and if so, how do you keep the trap from firing on the "expected" failures in case 1?
Running Bash 5.2 on Ubuntu 24.04. Any examples from real deploy scripts would be great.

0 Answers