The “always true” footgun
Most commonGitHub only evaluates what is inside ${{ }}. Operators left outside become literal text after substitution — a non-empty string, which is always truthy. The evaluator flags this (actions/runner#1173).
Always true
# ALWAYS TRUE — operators sit OUTSIDE ${{ }}
jobs:
deploy:
if: ${{ github.ref }} == 'refs/heads/main'
# after substitution this is the literal string
# refs/heads/main == 'refs/heads/main'
# a non-empty string => truthy => runs on EVERY branch Fixed
# CORRECT — wrap the WHOLE condition in one ${{ }}
jobs:
deploy:
if: ${{ github.ref == 'refs/heads/main' }}
# now GitHub evaluates the comparison, not a literal string