ci: 테스트 샤딩·잡 병렬화·커버리지 리포트 재사용으로 CI 단축 - #494
Conversation
check 한 잡이 설치→정적 검사→인프라 spec→전체 jest→빌드를 차례로 돌고, coverage-report가 jest를 한 번 더 돌림.
coverage-report의 기준 비교는 base에서 npm install이 실패해 head 결과를 기준으로 읽어 차이가 늘 0이었음(PR마다 약 70초 낭비).
- pr-check.yml을 static·scripts·build·test(2샤드, fail-fast 끔)·coverage-report·check로 분리
- test: --coverage 유지(LanguageService 모드 선택), 샤드별 임계는 '--coverageThreshold={}'로 끔, --json 결과를 아티팩트(1일)로
- coverage-report: 샤드 결과를 scripts/merge-coverage.ts로 병합 → 테스트 실패·샤드 누락·임계 미달이면 실패(report.json은 먼저 씀), 임계는 jest.config.js
- Codecov는 합친 lcov 1회(backend-lcov 그대로)
- push면 report.json을 기준 아티팩트(90일, continue-on-error)로, PR이면 이 레포 base 브랜치의 성공한 push 실행에서만 기준을 받음(base 커밋 우선 → 브랜치 최근 → 없으면 head 복사)
- 액션은 coverage-file·base-coverage-file로 테스트 재실행 없이 댓글만, 기준 경로 고정(댓글 중복 방지)
- check: if: always() + needs 결과가 전부 success인지 jq로 직접 검사(skipped·cancelled도 실패)
- actions: read는 coverage-report에만
- node_modules 캐시(yarn.lock·package.json·OS·arch·node 버전 키), 적중이면 설치 대신 prisma generate
- image·pr-title·concurrency·워크플로 이름 CI는 그대로
- istanbul-lib-coverage·-report·istanbul-reports(+@types)를 lockfile의 기존 버전 그대로 devDependencies에 명시
- README CI 절·가이드 §9·jest.config.js 주석을 새 구조로
테스트
- merge-coverage.spec(신규): 두 조각 병합 = 단일 실행 수치, 반증으로 조각 하나·샤드 누락·테스트 실패(report.json은 남음)·coverageMap 없음·지원 안 하는 임계 형식, CLI 종료 코드 0/1/2
- deploy-workflow.spec: check 집계 스크립트를 실제 bash로 돌려 success/failure/skipped/cancelled/빈 needs 전수, coverage-report needs·if, 샤드 수 일치, 캐시 키·적중 경로, actions 권한 범위, 기준 받기 스크립트를 gh 대역으로 돌려 순서·중복 제거·head 폴백
- 반증: 샤드 수 검사·테스트 실패 검사 제거, pct 비교 방향, jq success 조건, 중복 제거, coverage-report if, head 폴백을 각각 지우면 실패 확인
- 실제 coverage-final.json(500파일)을 파일 단위 두 조각으로 나눠 병합 → 10050/10267·3773/4087·2019/2075·9143/9283으로 원본과 일치, 한 조각만이면 임계 미달 exit 1
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🧹 knip — dead-code 리포트전체 리포트
|
🩺 NestJS Doctor — 90/100 (Excellent)진단 475건 (error 12).
architecture / security 상위 항목
|
Coverage report
Test suite run success3782 tests passing in 357 suites. Report generated by 🧪jest coverage report action from 2b150c1 |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 570e8cbe05
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| path: | | ||
| node_modules | ||
| .yarn/install-state.gz | ||
| key: node-modules-${{ runner.os }}-${{ runner.arch }}-node${{ steps.node.outputs.node-version }}-${{ hashFiles('yarn.lock', 'package.json') }} |
There was a problem hiding this comment.
Include Yarn configuration in the cache key
When a PR changes .yarnrc.yml or the repository’s Yarn release without modifying package.json or yarn.lock, this key remains unchanged, so CI restores the old node_modules tree and skips yarn install --immutable. Since .yarnrc.yml currently controls both nodeLinker and yarnPath, such a change can be tested against a stale installation or fail solely because the new layout was never installed; hash the Yarn configuration and release files into the key as well.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
반영: 4d05aa0 — 캐시 키 hashFiles에 .yarnrc.yml·.yarn/releases/** 추가(5개 잡), deploy-workflow.spec이 키 구성 고정. .yarn/patches는 lockfile의 patch 해시로 이미 반영.
- 첫 실행 로그에서 codecov-action이 CC_TOKEN 등을 GITHUB_ENV로 내보내 뒤 단계(기준 받기 gh 스크립트·서드파티 댓글 액션) env에 남는 것 확인
- 예전엔 Codecov(check)와 댓글 액션(coverage-report)이 다른 잡이라 같은 env에 없었음 → Codecov를 coverage-report 마지막으로 옮겨 그대로 분리
- deploy-workflow.spec의 run 블록 실행 셸을 bash -eo pipefail → bash -e로(shell 미지정 run의 실제 셸, 로그의 "/usr/bin/bash -e {0}")
- 집계·기준 받기 스크립트는 pipefail 유무와 무관하게 같은 결과(마지막 명령의 종료 코드로 판정)
테스트
- deploy-workflow.spec: Codecov 단계가 기준 받기·댓글 액션보다 뒤인지 고정
- 한국어 README와 같게: pr-check.yml 역할 문구, CI 잡 구성 절 추가, 전체 회귀·임계 담당 문장
- 96/86/92/96 → 97/92/97/98(실측 정수 내림, 사용자 결정) - 근거: 이 PR 첫 CI(run 37221170062)의 샤드 병합 수치 97.90/92.42/97.45/98.51 - 10063/10278·3797/4108·2026/2079·9154/9292 — 같은 트리를 단일 실행한 #492 리포트와 개수까지 일치 - 임계는 jest.config.js 한 곳(로컬 test:cov는 jest가, CI는 coverage-report의 merge-coverage가 같은 값으로 검사) - README·README.en·가이드 §9·jest.scripts.config.js 주석의 수치 갱신
- CI(GITHUB_ACTIONS=true)에서만 실패하는 spec 1건 — test 샤드 실패 시 coverage-report·check가 failure로 끝나는지 PR CI에서 확인하고 바로 되돌린다
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: adadfc972d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // 반증: 집계 잡 확인용 — 되돌림. CI(GITHUB_ACTIONS=true)에서만 실패해 test 샤드·coverage-report·check가 빨간색이 되는지 본다 | ||
| describe('반증: 집계 잡 확인용 — 되돌림', () => { | ||
| it('CI에서는 일부러 실패한다', () => { | ||
| expect(process.env.GITHUB_ACTIONS).toBeUndefined(); |
There was a problem hiding this comment.
Remove the CI-only failing falsification test
When the test shards run in GitHub Actions, GITHUB_ACTIONS is always set to true, so the shard containing this spec necessarily fails. The workflow then propagates that failure through coverage-report and the required check job, preventing every PR and push from receiving a successful CI result; revert this temporary falsification spec before merging.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
반영(의도된 임시 커밋): e8499c1에서 되돌림 — 집계 잡 반증용(run 37222298720에서 test (1)·coverage-report·check failure 확인). 최종 head 4d05aa0에는 없음.
This reverts commit adadfc9. - 반증 실행(run 37222298720) 결과: test (1) failure → coverage-report failure(merge-coverage가 테스트 실패로 exit 1, 댓글은 실패 내역으로 갱신) → check failure(skipped 아님)
- 리뷰 지적(Codex): .yarnrc.yml(nodeLinker·yarnPath)이나 .yarn/releases만 바뀌면 키가 같아 낡은 node_modules를 복원하고 설치를 건너뜀 - hashFiles에 '.yarnrc.yml', '.yarn/releases/**' 추가(5개 잡 동일), README·README.en 문구 갱신 - .yarn/patches는 lockfile의 patch 해시로 이미 키에 반영 테스트 - deploy-workflow.spec: 캐시 키에 yarn 설정·릴리즈가 들어가는지 고정
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4d05aa0d8a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| # 통과시킨다 — 그래서 if: always()로 늘 돌고 선행 결과가 전부 success인지 직접 본다. 건너뜀·취소도 실패다. | ||
| check: | ||
| if: always() | ||
| needs: [static, scripts, build, test, coverage-report] |
There was a problem hiding this comment.
Exclude read-only PR reporting failures from the required check
For fork-originated and Dependabot PRs, GitHub supplies a read-only GITHUB_TOKEN, and the coverage action documents that attempting to publish its comment then fails with Resource not accessible by integration (GitHub permissions, action documentation). Because check now requires coverage-report and rejects every result other than success, an otherwise valid external or Dependabot PR cannot satisfy the required check; conditionally omit the write-dependent reporting step for read-only PRs or keep that publishing failure outside the aggregate gate.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
미반영(기록): fork PR의 읽기 전용 토큰으로 댓글 단계가 실패하는 것은 이 PR 이전에도 같았음(coverage-report가 이미 필수 체크). Dependabot은 잡 단위 permissions가 적용돼 성공(run 37133178668, #478) — 해당 부분은 성립하지 않음.
- 샤드 아티팩트 보존 1일 → 7일: 하루 뒤 coverage-report만 재실행하면 샤드 0개로 실패하던 것
- check(집계)는 토큰을 쓰지 않아 permissions: {}
- 가이드 §9·pre-push-test-plan 주석의 "CI check가 돌린다"를 static·scripts·test 잡 기준으로
배경
check한 잡이 설치 → 정적 검사 → 인프라 spec → 전체 jest → 빌드를 차례로 돌립니다.coverage-report가 jest를 한 번 더 돌리므로, PR 필수 체크가 끝나기까지 최근 PR 5건(fix: 리뷰 목록이 cursor null을 첫 페이지로 처리 #487~fix: 자격증명 버전으로 변경과 겹친 로그인·회전 세션 차단, 블랙리스트 ms 비교 #492) 기준 373~448초가 걸렸습니다.check370445초,381초coverage-report316coverage-report의 기준 비교는 실제로 동작한 적이 없습니다.npm install을 돌리다 실패하고(yarn 레포), 남아 있던 head 결과를 기준으로 읽어 차이가 늘 0으로 표시됩니다.변경
pr-check.yml을 잡으로 나눠 나란히 돌립니다. 워크플로 이름CI와 필수 체크 이름 4개는 그대로입니다.static: codegen, tsc, lint, dto:check(경고만), docs:check, arch:checkscripts:test:scriptsbuild: 빌드 2회 +dist/main.js확인(캐시 회귀 검출 유지)test: jest 2샤드(fail-fast: false)--coverage는 LanguageService 모드 선택 때문에 유지하고, 샤드별 임계는'--coverageThreshold={}'로 끕니다.--json결과를 아티팩트(1일)로 올립니다. 로그가 길어지지 않게 샤드의 커버리지 리포터는text-summary만 씁니다.coverage-report: 샤드 결과를scripts/merge-coverage.ts로 합칩니다.report.json은 먼저 써서 PR 댓글에 실패가 보입니다. 임계는jest.config.js에서 읽습니다.report.json을 기준 아티팩트(90일)로 올립니다. 실패해도 배포를 막지 않게continue-on-error입니다.coverage-file·base-coverage-file로 테스트를 다시 돌리지 않고 댓글만 답니다.backend-lcov그대로). codecov-action이 업로드 토큰을GITHUB_ENV로 뒤 단계에 남기므로 잡의 마지막 단계에 둡니다.check:if: always()로 늘 돌고,needs결과가 전부success인지 직접 검사합니다. 건너뜀·취소도 실패입니다.actions: read는 기준 아티팩트를 받는coverage-report에만 줍니다.node_modules를 캐시합니다. 키는yarn.lock·package.json·.yarnrc.yml·yarn 릴리즈·OS·arch·node 버전입니다. 적중하면 설치 대신prisma generate만 돌립니다(postinstall 대체).jest.config.js·jest.scripts.config.js주석을 새 구조와 수치에 맞게 고쳤습니다.검증
448초 대비 약 2.53분 줄었습니다. 대기 경로는test샤드(약 3분 10초3분 40초) →40초) →coverage-report(30check(2초)입니다.130초, 미적재 파일 커버리지·리포트 1317초입니다. 같은 spec도 러너에 따라 25~40% 차이가 납니다.test (1)failure,test (2)success,coverage-reportfailure(merge-coverage가 "테스트 실패 — 실패 1건"으로 exit 1),checkfailure(needs: test=failure, coverage-report=failure)merge-coverage.spec(신규)deploy-workflow.speccheck집계 스크립트를 실제 bash로 돌려 success·failure·skipped·cancelled·빈 needs를 전수 확인합니다.coverage-final.json(500파일)을 파일 단위로 두 조각 내 CLI로 합쳤습니다.리뷰 반영
check는 토큰을 쓰지 않아permissions: {}pre-push-test-plan주석의 "CIcheck가 돌린다"를 새 잡 기준으로 정리.yarnrc.yml·yarn 릴리즈 포함(Codex P2)플랜 대조
test:scripts)dist/main.js확인 유지)fail-fast: false,--coverage유지,'--coverageThreshold={}',--json·--testLocationInResults, 아티팩트 1일needs: test, PR·push 모두 실행jest.config.js, report.json을 먼저 씀backend-lcov)continue-on-errorcoverage-file·base-coverage-fileif: always(), 결과가 전부 success인지 직접 검사yarn.lock·package.json·OS·node), 적중 시prisma generate만.yarnrc.yml·yarn 릴리즈도 키에 추가CI·파일pr-check.yml유지usesSHA 고정,run:안${{ }}금지, 모든 잡에 stepsactions: read