
PR이랑 push를 한 워크플로우에 우겨넣으면 커버리지가 이중으로 올라가거나 검증이 빠지는 구멍이 생긴다. 역할별로 두 개로 쪼개고 path filter까지 살짝 얹어주면 머리도 편하고 CI 실행 시간도 줄어든다.
팀 프론트 저장소에 CI를 새로 깔면서 기존 저장소들을 구경하는데, 어떤 곳은 워크플로우가 2개였고 어떤 곳은 1개였다. opened만 쓰는 곳, synchronize까지 쓰는 곳도 섞여 있었다. "이게 다 무슨 차이지?" 싶어서 한번 정리해봤다.
이름만 보면 비슷해 보이지만, 둘은 성격이 꽤 다르다.
| 항목 | push | pull_request |
|---|---|---|
| 트리거 시점 | 커밋이 브랜치에 push될 때 | PR 생성/업데이트 시 |
GITHUB_SHA | push된 커밋 그 자체 | PR의 머지 시뮬레이션 커밋 |
GITHUB_REF | refs/heads/브랜치명 | refs/pull/NUMBER/merge |
| 테스트 대상 | 현재 브랜치 코드 | base와 머지했을 때의 코드 |
pull_request는 좀 똑똑한 친구라서 "이 PR 머지되면 어떻게 될까?"를 미리 시뮬레이션한 커밋을 만들어놓고 돌린다. 그래서 PR 검증엔 이쪽이 훨씬 어울린다.
1) test_and_coverage.yml (push 트리거)
on:
push:
branches:
- master
- develop
- 'feature/*/main'
paths:
- 'src/**'
- 'package.json'
- 'yarn.lock'
- 'tsconfig*.json'
- 'vitest.config.*'
역할은 테스트 돌리고 커버리지를 Codecov에 업로드하는 것. 커버리지 숫자는 base 브랜치 기준으로 쭉 쌓여야 트렌드가 보이는데, PR마다 올려버리면 base 숫자가 흔들려서 영 의미가 없어진다.
2) pull_request_stats.yml (pull_request 트리거)
on:
pull_request:
types: [opened, synchronize]
이쪽은 Lint, Test, Build를 병렬로 돌리는 PR 게이트키퍼. 개발자가 PR 페이지 열었을 때 초록불/빨간불 바로 보여주는 담당이다.
opened만? 아니면 synchronize까지?여기가 은근히 재밌는 설계 갈림길이다.
| 패턴 | pull_request_stats 트리거 | 조건 |
|---|---|---|
| A | [opened]만 | test_and_coverage가 모든 push를 커버할 때 |
| B | [opened, synchronize] | test_and_coverage가 특정 브랜치만 커버할 때 |
패턴 A에서는 feature 브랜치에 push해도 test_and_coverage가 알아서 잡아주니까 synchronize가 없어도 괜찮다. 반대로 패턴 B에서는 master/develop/feature/*/main 같은 주요 브랜치만 잡기 때문에, 일반 feature 브랜치로 push한 커밋은 아무도 검증하지 않는 빈틈이 생긴다. 이걸 synchronize 없이 놔두면 PR 처음 열 때만 검사하고 그다음 커밋부턴 무사통과되는 사태가 벌어진다.
즉, test_and_coverage가 커버하는 브랜치 범위가 pull_request_stats의 트리거를 결정한다. 이 한 쌍이 핵심 축.
GitHub 공식 문서에서도 밀어주는 기능인데, 꽤 쏠쏠하다. README 한 줄 고치는데 테스트 30분 돌릴 이유가 없잖아?
paths:
- 'src/**' # 소스 코드
- 'package.json' # 의존성
- 'yarn.lock' # 의존성 락파일
- 'tsconfig*.json' # TS 설정
- 'vitest.config.*' # 테스트 설정
단, 함정이 하나 있다. branches랑 paths를 같이 쓰면 둘 다 만족할 때만 실행된다. 이 규칙 모르면 "아니 왜 내 워크플로우가 안 돌지?" 하고 한참 헤매게 된다. (경험담 아님.)
|| true로 lint 실패 삼키지 말기: biome ci src || true 이렇게 쓰면 lint가 뭐라 해도 job은 초록불이다. 경고만 띄우고 싶으면 continue-on-error: true 쪽이 의도가 훨씬 선명하다.npm install --global yarn은 Yarn 4에선 안 먹힌다. corepack enable + yarn install --immutable 조합으로 갈아줘야 한다.test_and_coverage의 브랜치 범위랑 pull_request_stats의 synchronize 포함 여부, 이 한 쌍이 전체 설계의 핵심 축이다.