블로그
devlog — bash

$ cat 2026-02-22-entry.md

date: 2026-02-22read: ~2min

CI는 통과했는데 프로세스가 죽던 밤, exit 137 완전 정복기

⚡ CI에서 테스트는 전부 통과했는데도 작업이 실패 처리되던 종료 경로 버그를 잡아내고 스모크 파이프라인을 안정화했습니다 🙌

한 줄 요약

문제는 앱 로직이 아니라 정리 스크립트였고, 포트 정리 단계의 자기-프로세스 종료를 차단해 CI를 정상 green으로 복구했습니다.


오늘 한 일(핵심만)

  • 🔍 GitHub Actions 실패 로그를 추적해 실제 실패 지점이 테스트 본문이 아닌 종료 단계임을 확인
  • ✂️ lib/devlog.ts에서 Notion 장애 시 API 계약을 유지하도록 안전 반환 경로를 추가
  • ⚡ scripts/kill-port.mjs를 LISTEN 소켓 대상 + self/parent PID 제외로 수정해 exit 137을 제거
  • ✅ 로컬 check/스모크, PR CI(check/smoke)까지 모두 통과

왜 이게 중요하냐면 (Motivation & Impact)

품질 게이트가 흔들리면 코드 품질과 무관하게 배포가 막힙니다. 이번 작업으로 테스트 신호의 신뢰성을 복구해 이후 작업 속도를 지켰습니다.

문제는 이랬어요 (Issue & Debugging)

Playwright는 통과했는데 종료 직전에 Killed + ELIFECYCLE exit code 137이 발생했습니다. 포트 정리 스크립트가 종료 대상을 과하게 잡아 자기 프로세스를 죽이는 경로가 원인이었습니다.

지금은 이렇게 바뀌었어요 (Solution)

typescript
// scripts/kill-port.mjs
const output = run("lsof", ["-nP", "-iTCP:" + String(port), "-sTCP:LISTEN", "-t"]);
return parseUnixPidsFromLsof(output, [process.pid, process.ppid]);

핵심은 LISTEN 대상만 종료하고 현재 실행 중인 프로세스 계열을 제외한 점입니다.

오늘의 체크(가볍게)

  • ✅ PR #4 기준 check/smoke/Vercel 모두 pass
  • ⚠️ Notion MCP는 인증 필요 상태여서 종료 기록은 Notion REST API로 대체 수행

마무리

오늘은 기능 추가보다 품질 신호를 믿을 수 있게 만든 것이 핵심이었습니다 🔧

댓글로 남겨줘: 여러분 팀 CI에서 가장 자주 터지는 "가짜 실패"는 무엇인가요? #CI #E2E #GitHubActions #Stability