테스트가 실패했는데 step이 초록색으로 통과한 이유 — shell을 적지 않으면 pipefail이 꺼져 있다
문제 발생
테스트 출력을 파일로도 남기려고 tee를 붙여 둔 step입니다.
- name: Test
run: pnpm test | tee test.log테스트가 실패해 pnpm test가 0이 아닌 종료 코드를 내도 step은 초록색으로 통과합니다. 로그에는 실패한 테스트가 그대로 찍혀 있는데 job은 성공이고, 뒤의 step까지 이어서 실행됩니다.
원인 분석
파이프라인의 종료 상태는 마지막 명령의 것입니다. Bash 매뉴얼에 따르면 pipefail 옵션이 켜져 있지 않은 한 파이프라인의 종료 상태는 파이프라인의 마지막 명령의 종료 상태입니다. pnpm test가 실패해도 tee가 성공하면 전체는 0입니다.
그리고 shell을 적지 않은 step에는 pipefail이 없습니다. GitHub 문서의 셸 표가 두 경우를 구분합니다.
shell미지정(Linux/macOS):bash -e {0}shell: bash:bash --noprofile --norc -eo pipefail {0}
문서는 미지정일 때 "bash를 명시했을 때와 다른 명령을 실행한다"고 따로 적어 둡니다. sh와 bash 모두 set -e로 fail-fast가 걸리지만, 0이 아닌 종료 상태를 내는 파이프라인에서 일찍 끝내는 -o pipefail은 shell: bash를 지정했을 때만 추가됩니다.
-e가 걸려 있어도 파이프라인 전체의 종료 상태가 tee의 0이므로 step은 실패할 이유가 없습니다.
해결 방안
- 워크플로 기본 셸을 명시합니다.
defaults.run은 워크플로의 모든runstep에 기본shell을 줍니다.
defaults:
run:
shell: bashjob 하나에만 적용하려면 jobs.<job_id>.defaults.run에 적습니다. 같은 이름의 기본값이 여러 곳에 있으면 더 구체적인 쪽이 쓰입니다.
-
한 step만 고치려면 그 step에
shell: bash를 적습니다. step의shell키는 러너 OS의 기본 셸과 job의 기본값을 덮어씁니다. -
켠 뒤에 달라지는 종료 상태를 알아 둡니다.
pipefail이 켜지면 파이프라인의 반환 값은 0이 아닌 상태로 끝난 마지막(가장 오른쪽) 명령의 값이고, 모든 명령이 성공했을 때만 0입니다. 지금까지 앞쪽 명령의 실패를 조용히 넘기던 파이프가 있었다면 이제 step을 실패시킵니다. 실패해도 되는 명령이면|| true처럼 의도를 코드에 적습니다. -
옵션을 직접 쥐려면 템플릿 문자열을 씁니다. 문서에 따르면
shell: bash {0}처럼 템플릿 문자열을 주면 셸 파라미터를 전부 직접 정할 수 있습니다. 이때는 GitHub가 붙여 주던-e도 빠지므로 스크립트 첫 줄에set -eo pipefail을 직접 적습니다. -
일부러 실패시켜 확인합니다. Linux 러너에서 아래 step은
shell을 적지 않으면 통과하고,shell: bash에서는 실패해야 합니다.
- run: false | catWindows 러너는 기본 셸이 pwsh라 같은 표의 다른 줄이 적용됩니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.